jstrecker's picture

Jaymie (@jstrecker)

Groups

  • Vuo Founder
  • Team Vuo
jstrecker's picture
Jaymie commented on NoodleDesign's Discussion, “SMPTE timecode

Well, it's not quite that simple. The SMPTE timecode itself is just a number (containing the hours, minutes, seconds, and frame/sample count). But that number has to be encoded in some kind of signal that the receiving A/V equipment knows how to use (audio, video, or MIDI).

This article gives a little background on SMPTE and suggests a way that you might be able to send LTC SMPTE from Vuo. Use the LTC Two web app to generate a wav file. Then play the wav file in Vuo (see File > Open Example > Audio > Play Audio File).

For generating and encoding the SMPTE timecode within Vuo, there's an open feature request for one method: MIDI Clock / MTC.

jstrecker's picture
Jaymie commented on Bodysoulspirit's Discussion, “Help with stateful nodes

Once you get used to pointers and certain patterns become familiar, it gets easier :) You don't have to put all of your conscious focus into this being the address of that, you just know in the back of your mind.

The vuo.math.count node class's code is actually more complicated than it needs to be. There was a limitation in earlier versions of Vuo that meant we had to use more pointers than would otherwise be necessary. When we fixed that, we didn't go back and simplify the vuo.math.count code. (Now that you've brought it to our attention, we will.)

First, here's an annotated version of your code, since you've already familiar with it. I've left in the comments for the parts you already understood.

// Define the `nodeInstanceInit` function. It returns a value of type `VuoGenericType1 *`.
// Think of it like this:
//
// VuoGenericType1 * nodeInstanceInit(VuoGenericType1 setCount)
// {
//    // … body of the function …
// }
// 
// The `VuoInputData` is special syntax required by the Vuo compiler to recognize a parameter as an input port.
VuoGenericType1 * nodeInstanceInit
(
        // Function parameter of type `VuoGenericType1` named `setCount`.
        VuoInputData(VuoGenericType1) setCount
)
{
    // Declare a variable of type `VuoGenericType1 *` named `countState`.
    // Allocate a piece of memory the right size to hold a `VuoGenericType1` value. (The memory just holds uninitialized data at this point.)
    // Set `countState` to the address of that memory.
    VuoGenericType1 *countState = (VuoGenericType1 *) malloc(sizeof(VuoGenericType1));
 
    // Register `countState` with Vuo's reference-counting system.
    // Later during the composition execution, when the reference count of `countState` goes down to 0, the `free` function will be called to deallocate `countState`.
    // See https://api.vuo.org/latest/group___managing_memory.html
    VuoRegister(countState, free);
 
    // Copy the value of `setCount` into the piece of memory that `countState` points to.
    *countState = setCount;
 
    return countState; // Return the pointer
}
 
// Define the `nodeInstanceEvent` function.
// Think of it like this:
//
// void nodeInstanceEvent(VuoGenericType1 **countState, VuoGenericType1 increment, bool incrementEvent, VuoGenericType1 setCount, bool setCountEvent, VuoGenericType1 *count)
// {
//    …
// }
//
// The `VuoInstanceData`, `VuoInputEvent`, etc. are special syntax required by the Vuo compiler.
void nodeInstanceEvent
(
        // Function parameter of type `VuoGenericType1 **` named `countState`.
        // The `VuoInstanceData` adds an extra layer of pointers, making `countState` an output parameter.
        // See https://stackoverflow.com/questions/42403940/c-input-and-out-parameters-of-a-function .
        // The value of `*countState` in this function is the same as the value returned from the `nodeInstanceInit` function.
        VuoInstanceData(VuoGenericType1 *) countState,
 
        VuoInputData(VuoGenericType1, {"defaults":{"VuoInteger":1, "VuoReal":1.0}}) increment, // Ok I get this, we declare a Vuo Port
        VuoInputEvent({"eventBlocking":"none","data":"increment"}) incrementEvent, // Here we declare event blocking, ok
        VuoInputData(VuoGenericType1, {"defaults":{"VuoInteger":0, "VuoReal":0.0}}) setCount, // Another port, ok
        VuoInputEvent({"eventBlocking":"none","data":"setCount"}) setCountEvent, // Ok
 
        // Function parameter of type `VuoGenericType1 **` named `count`.
        // The `VuoOutputData` adds an extra layer of pointers, making `count` another output parameter.
        VuoOutputData(VuoGenericType1) count // Ok, this is the ouput port of the node
)
{
    if (incrementEvent) // Ok, each time the node receives an event do ...
        // Add `increment` to the count stored in the node instance data.
        // `**countState` is the piece of memory allocated in `nodeInstanceInit`.
        // `*countState` is a pointer to that memory.
        // `countState` is a pointer to a pointer to that memory.
        **countState += increment;
    if (setCountEvent) // when this receives en event, do ...
        // Set the count stored in the node instance data to `setCount`.
        **countState = setCount;
 
    // Set the output port's value to the count stored in the node instance data.
    // `*count` is the piece of memory where the output port value is stored (created by Vuo outside of this function).
    // `count` is a pointer to that memory (passed into this function by Vuo).
    *count = **countState;
}

Here's a simplified version with one fewer level of pointers for the node instance data:

// Think of it as:
// 
// VuoGenericType1 nodeInstanceInit(VuoGenericType1 setCount)
// {
//    …
// }
VuoGenericType1 nodeInstanceInit
(
        VuoInputData(VuoGenericType1) setCount
)
{
    return setCount;
}
 
// Think of it as:
//
// void nodeInstanceEvent(VuoGenericType1 *countState, VuoGenericType1 increment, bool incrementEvent, VuoGenericType1 setCount, bool setCountEvent, VuoGenericType1 *count)
// {
//    …
// }
void nodeInstanceEvent
(
        // Function parameter of type `VuoGenericType1 *` named `countState`.
        // The first time this function is called, `*countState` is the value that was returned from `nodeInstanceInit()`.
        VuoInstanceData(VuoGenericType1) countState,
 
        VuoInputData(VuoGenericType1, {"defaults":{"VuoInteger":1, "VuoReal":1.0}}) increment,
        VuoInputEvent({"eventBlocking":"none","data":"increment"}) incrementEvent,
        VuoInputData(VuoGenericType1, {"defaults":{"VuoInteger":1, "VuoReal":1.0}}) decrement,
        VuoInputEvent({"eventBlocking":"none","data":"decrement"}) decrementEvent,
        VuoInputData(VuoGenericType1, {"defaults":{"VuoInteger":0, "VuoReal":0.0}}) setCount,
        VuoInputEvent({"eventBlocking":"none","data":"setCount"}) setCountEvent,
        VuoOutputData(VuoGenericType1) count
)
{
    if (incrementEvent)
        *countState += increment;
    if (decrementEvent)
        *countState -= decrement;
    if (setCountEvent)
        *countState = setCount;
    *count = *countState;
}
jstrecker's picture
Jaymie commented on eledtech's Discussion, “FFGL Inputs

You could do that using the FFGL SDK and the Vuo SDK together.

Probably a good starting point would be to build something with each SDK individually. With the FFGL SDK, build an FFGL plugin that works in Resolume. With the Vuo SDK, build an application that runs a Vuo composition.

Once you've gotten a simple example working with each SDK to make sure you have the basics down, you could then add code into the FFGL plugin to make it run a Vuo composition. The process would be something like this:

  • Use the Vuo SDK's command-line tools to compile and link the composition into a dylib.
  • Copy VuoRunner.framework and the above dylib into the FFGL plugin bundle.
  • Use VuoRunner::newCurrentProcessRunnerFromDynamicLibrary() to launch the precompiled composition dylib.
  • Use VuoRunner::setPublishedInputPortValues(), VuoRunner::firePublishedInputPortEvent(), VuoRunner::waitForFiredPublishedInputPortEvent(), and VuoRunner::getPublishedOutputPortValue() to pass data between the FFGL plugin and the Vuo composition.
  • Render the output image into the FFGL framebuffer.
jstrecker's picture

Thanks for letting us know about this problem, Alastair. It's similar enough to this other bug report that I'm going to merge this one into it. The same fix will most likely fix both issues.

jstrecker's picture
Jaymie commented on eledtech's Discussion, “FFGL Inputs

Great that you're exploring the Vuo SDK!

Unfortunately, none of the examples you mentioned are currently possible. The first two would require changes to Resolume, and the third would require changes within Vuo.

Color input — As you know, for FFGL plugins, Resolume shows a color picker with separate RGBA channels rather than the native color picker used for built-in effects. The Resolume developers would need to make a change to either the FFGL spec or Resolume in order for FFGL plugins to show the native color picker.

Multi-line text — Vuo's text input ports support multiple lines, but Resolume doesn't seem to have a way to enter linebreaks in a text field. The Resolume developers would need to make that change.

Switch/list — This one could be done with a change to Vuo. The FFGL spec already supports inputs with a list of options to choose from. We would need to update Vuo to be able to set up options for a published input port. You're welcome to create a feature request.

Pages