Programming Custom Blocks

From Patchblocks
Jump to: navigation, search

Blocks are the basic components that compose a Patch. They are defined by XML files that are a mixture of data definitions, metadata, and functions written in the C programming language.


XML File[edit]

The Root of the XML file is the <block> element. One block may be included in each file, which should have the .xml file extension. Blocks must contain the following children:

  • <category>
  • <info>
  • <help>
  • <data>
  • <function>

Metadata[edit]

Name[edit]

The name attribute of the <block> specifies what text appears on the block in the editor.

Info[edit]

The <info> element defines the tooltip that appears in the editor.

Category[edit]

Blocks in the PatchBlocks editor's Browser window are divided into categories. The <category> element determines where the block appears in this window. The category can be one of the built-in categories, or any other string, such as utilites.

Built-in Categories[edit]

The blocks shipped with the PatchBlocks editor are divided into the following categories:

  • Effects
  • Generators
  • InOut
  • Math
  • MIDI
  • Routing
  • Sequencing

Help[edit]

In-editor help is defined by a <![CDATA[ ]]> comment block that is a child of the <help> element. The CDATA text is an HTML snippet that is rendered in the help window of the editor.

Example[edit]

This is the metadata of the Multiply block:

<block name="Multiply">
        <category>Math</category>
        <info>Multiplies two values</info>
... the block functionality is described here
        <help>
        <![CDATA[
This block multiplies two values and sends out the result.
        ]]>
        </help>
</block>

Data Element[edit]

The <data> element defines a struct that contains variables for accessing block inputs and outputs, as well as storing persistent data. The element has the name attribute, and multiple <variable> child elements.

Variables[edit]

The variable element defines a variable that can either be an input, output, or user data. All variables have some attributes in common:

  • value, which specifies an initial value
  • info, which will appear in a tooltip for inputs and outputs
Inputs and Outputs[edit]

A block has inputs, which accept data from other blocks, and outputs, that pass data on to other blocks.

Inputs and outputs are declared by <variable> elements that have the socket attribute, which can be "in" or "out". All of the input variables will be of type int32_t * and all of the outputs are int32_t. The values pointed to and contained by these values are assumed to be fixed-point numbers.

Input variables support the editable attribute, which determines if the user is able to enter values for the variable in the editor's inspector panel. If editable is 0, the user will only be able to connect a wire from another block. This variable is a clock pulse, so it doesn't make sense for the user to edit it:

<variable socket="in" info="Clk In"/>

These variables are available in the function code through the data struct pointer, and are named by direction and zero-indexed sequence, e.g.: *data->in0, *data->in1, data->out0 and data->out1.

User Data[edit]

Persistent variables can be defined with the dtype and name attributes. The data type must be a valid C data type, and the name must be a unique, valid C identifier. Variables can be initialized with the value attribute, which will assign a fixed-point value to the variable.

These variables are also stored in the data struct, and have the identifier specified in the <variable> Element. For example, the following <variable> specifies an array of 42 32-bit integers named buffer as a struct member:

    <variable dtype="int32_t" name="buffer[42]" />

Example[edit]

Here is the <data> contents of the Multiply block:

<data name="i2o1">
    <variable socket="in" editable="1" value="1" info="Value 1"/>
    <variable socket="in" editable="1" value="1" info="Value 2"/>
    <variable socket="out" info="Result" />
</data>

Function Element[edit]

Lastly, the functionality of the block is described using the <function> element. The name attribute specifies the function's name in the C code, and the function body is specified in the CDATA content of the element. The function declaration is handled by the preprocessor, so exclude it and the closing brace.


For each instance of a block in the patch, for every sample, the block's function is called. It is passed the data struct pointer, which is unique to each block instance.


In the function body, data is a pointer to a struct, input members are pointers to int32_t, and output members are int32_t. Other declared variables will have the type specified in the data declaration. For example, here is valid C code that sums a series of numbers:

    data->sum += *(data->in0);
    data->out = data->sum;

The parentheses around data->in0 are for clarity, and are not necessary. As in the example, always use -> to access struct members, and always dereference input pointers with *.

Additionally, the function element has a rate attribute, that set the sample rate of the block when placed in the editor. The default will be audio rate unless set to "control" for control rate.

Example[edit]

Here is the <function> tag from the Multiply block:

    <function name="i2o1_multiply">
    <![CDATA[
        if((*data->in0 > 46340) && (*data->in1 > 46340)){
                data->out0 = (*data->in0 >> 5) * (*data->in1 >> 5);
        }else if(*data->in0 > 46340){
                data->out0 = ((*data->in0 >> 5) * *data->in1) >> 5;
        }else if(*data->in1 > 46340){
                data->out0 = (*data->in0 * (*data->in1 >> 5)) >> 5;
        }else{
                data->out0 = (*data->in0 * *data->in1) >> 10;
        }
    ]]>
    </function>

Programming Conventions[edit]

Fixed Point Representation[edit]

The input and output values use Q22.10 fixed-point representation. In this system, a number is stored in two parts: the integer part and the fraction part. In Q22.10, the 10 lowest-order bits represent the fraction, while the 22 highest-order bits represent the integer.

The integer part is a 22-bit signed integer and is stored in the upper bits. The fraction is in the lower 10 bits and is a fraction of 1024.

For example, the number 1.0 is stored as:

0000 0000 0000 0000 0000 0100 0000 0000 (Hex 0x00000400)

While the number 1.5 is:

0000 0000 0000 0000 0000 0110 0000 0000 (Hex 0x00000600)

Negative numbers are represented by the two's complement of the upper 22 bits. -0.5 is:

1111 1111 1111 1111 1111 1110 0000 0000 (Hex 0xFFFFFE00)

And -1.5 is:

1111 1111 1111 1111 1111 1010 0000 0000 (Hex 0xFFFFFA00)

Signals[edit]

Typically, audio signals inside Patchblocks range from -0.5 to 0.5. Above, we can see that it takes 11 bits to account for these numbers. However, the top bit is only used by the highest and the lowest values. This means that at 11 bits, half of the values are unused. Therefore, we can truncate the very highest and very lowest values of a signal input to use only 9 fractional bits and 1 sign bit. This can be accomplished simply with conditions:

if (*data->in0 > 0x1FF)
    *data->in0 = 0x1FF;
if (*data->in0 < -0x1FF)
    *data->in0 = -0x1FF;

This yields a 2's complement signed integer that ranges from -512 to 511. Keep in mind that it is stored in an int32_t but you only need an int16_t to store it long term.

Converting to and from Integers[edit]

Sometimes, you want to deal directly with the integer portion of the number. For example, you may want accept a count from the user. To convert inputs to an integer, you must divide by 1024, removing the fractional component. Dividing by 1024 is the same as right shifting 10 bits except that it also works on signed integers.

    int32_t in0_int = *data->in0 / 1024;

To convert integers to output data, simply reverse the operation and left-shift 10 bits.

    int32_t out0_int = 1;
    data->out0 = out0_int << 10;