Custom Object Baby Steps


#1

I've just started working on a new object. Trying to remember some of the C I learnt many years ago.

First question:

how do I declare and then initialise an array of signed 32bit integers?

I currently have:

<code.declaration>
    <![CDATA[
      int32_t pattern[32];
      int counter;
      int ntrig;
      int rtrig;
    ]]>
</code.declaration>
<code.init>
   <![CDATA[
      pattern[] = {-57, -55, -42, -40, -39, -30, -28, -25, -24, -22, -21, -20, -17, -15, -8, -3, -2, 3, 5, 7, 8, 11, 18, 20, 36, 39, 44, 47, 48, 49, 60, 63};
      counter = 0;
      ntrig = 0;
      rtrig = 0;
  ]]>
</code.init>

but get these errors:

/Users/alx/Documents/axoloti/build/xpatch.cpp:81:13: error: 'int32' does not name a type
             int32 pattern[32];
             ^
/Users/alx/Documents/axoloti/build/xpatch.cpp: In member function 'void rootc::instancetbnmpgen__1::Init(rootc*)':
/Users/alx/Documents/axoloti/build/xpatch.cpp:91:13: error: 'pattern' was not declared in this scope
             pattern[32] = {-57, -55, -42, -40, -39, -30, -28, -25, -24, -22, -21, -20, -17, -15, -8, -3, -2, 3, 5, 7, 8, 11, 18, 20, 36, 39, 44, 47, 48, 49, 60, 63};
             ^
/Users/alx/Documents/axoloti/build/xpatch.cpp: In member function 'void rootc::instancetbnmpgen__1::dsp(int32_t, int32_t, int32_t&)':
/Users/alx/Documents/axoloti/build/xpatch.cpp:111:30: error: 'pattern' was not declared in this scope
                 outlet_out = pattern[counter];
                              ^
make: *** [/Users/alx/Documents/axoloti/build/xpatch.bin] Error 1
shell task failed, exit value: 2
Compiling patch failed ( untitled )

a|x


#2

Inline array initialization can only be done at declaration

<code.declaration><![CDATA[
   int32_t pattern[32] = {-57, -55, -42, -40, -39, -30, -28, -25, -24, -22, -21, -20, -17, -15, -8, -3, -2, 3, 5, 7, 8, 11, 18, 20, 36, 39, 44, 47, 48, 49, 60, 63};
]]>
</code.declaration>

#3

Thanks johannes. So once declared, I can only set values using pattern[index] notation, then?

I have another stupid question.

How can I declare a function that is run once when the object is initialised, but can also be run at any time 'in the loop'?

Also, are values from knobs etc. available in the init block?

If not, I guess I will need to set some kind of switch variable so I can tell in the main loop that the object is running for the first time.

Is there some kind of 'hook' to hang a function on when parameter changes occur, or will I have to keep the values of the controls from the last iteration of the loop, and compare to current values, to work out if they've changed?

Finally, is it possible to set min, max and step-size for object controls?

Sorry for all the questions.

a|x


#4

Coming from a background in JavaScript, where variables can be of any type, and can even change their type dynamically, I'm afraid I'm a bit bewildered by the different data types I see in the library .axo objects.

I'm trying to make a simple k-rate object that steps through the values in an array and outputs them. Here's what I have so far:

<objdefs>
   <obj.normal id="tbnmpgen" uuid="f4aa3eb163415b6fc722e576dde754a226a022ac" sha="6f50fda64d42de1ad8230b4a833a42c03c18e494">
      <sDescription>Pseudo-random (repeatable) pattern generator. Based on Nord modular PatternGen module.</sDescription>
      <author>toneburst</author>
      <license>BSD</license>
      <inlets>
         <bool32.rising name="trig" description="trigger"/>
         <bool32.rising name="r" description="reset"/>
      </inlets>
      <outlets>
         <frac32.positive name="out" description="pattern"/>
      </outlets>
      <displays/>
      <params/>
      <attribs>
          <spinner name="length" MinValue="1" MaxValue="32" DefaultValue="16"/>
      </attribs>
      <code.declaration>
          <![CDATA[
            int32_t pattern[32] = {0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63,0,63};
            int counter;
            int ntrig;
            int rtrig;
          ]]>
    </code.declaration>
    <code.init>
         <![CDATA[
            counter = 0;
            ntrig = 0;
            rtrig = 0;
        ]]>
    </code.init>
    <code.krate>
        <![CDATA[
            if (inlet_trig>0) {
                if ((counter>attr_length - 1) || (inlet_r>0)) {
                    counter = 0;
                } else {
                    counter++;
                }
            }
            outlet_out = pattern[counter];
        ]]>
    </code.krate>
    </obj.normal>
</objdefs>

This appears to do absolutely nothing. I've setup a toggle object to feed the trigger inlet, and piped the outlet to a scope, and also to the Pitch inlet of a filter, but the scope shows no change when I press the button.

I suspect this is to do with variable types or ranges, but I can't figure it out. Could someone lend a hand?

Cheers,

a|x


#5

yes

you can put the funcion in <code.declaration>

currently there are only a few predefined functions to pre-scale parameters, no custom setter functions yet.

parameters are not available, only attributes

.
integer-type parameters have min/max, but always 1 for step-size
fractional-type parameters are continuous.

The scaling is off.
Either change the outlet type to int32.positive, or scale the values in your array like
{0<<21,63<<21,0<<21,...

<<x means shift x decimal places to the left, which is the equivalent of multiplying with 2^x.

And then you'll notice that the inlet_trig is still level-sensitive where you probably expect it to be edge-sensitive. Currently this is the responsibility of the object to detect the rising edge.


#6

I thought it was probably to do with scaling.

Would you recommend outputting floating-point number (in the range -64 to 64?), or unsigned 32bit integers?

I'm confused by the fact that the documentation states that blue outlets should output numbers in the -64 to +64 range, but when I look at the LFO objects, they all seem to output int32.positive (which I assume to be unsigned positive integers).

Are outputs automatically converted to floats in the -64 to +64 range? Or is the -64 to +64 range merely a convention in the Patcher UI, and when actually outputting values from an object, we should use the 32bit fixed-point integers (seem to remember reading that you recommended that, somewhere in the forum, but can't recall where now)?

Sorry for the stupid questions. I have quite a lot of experience working with high-level scripting languages (mostly JavaScript and PHP, but also some GLSL), but no formal background in computer science, so I'm not used to having to deal with issues like these. I'm keen to learn though, if it means I can make cool music tools on an exciting platform like Axoloti.

a|x


#7

This confuses me, too, I'm afraid.
The '<<' operator is a left bit-shift, if I'm not mistaken.
Why shift 21 bits though? Currently I have integers in the 0 > 64 range in my array. That range can be represented in 6 bits, I think. 6 + 21 = 27 bits. But, I'd be outputting a 32 bit integer, so what's happened to the other 5 bits?

Sorry again to keep asking stupid questions.

a|x


#8

Oh, I see in this thread that outputs are in the range -0x08000000 to 0x07FFFFFF for frac32 and 0x0 to 0x07FFFFFF for frac32.positive.

Do you have a list of all the possible inlet/outlet types, their ranges, and and what they're used for?

Just doing a multi-file search of the factory Objects directory for 'frac32', I'm seeing

frac32
frac32.vu
frac32.u.map
frac32.s.map
frac32.positive
frac32.bipolar
frac32buffer

a|x


#9

Is it planned that the inlet type 'bool32.rising' will eventually work such that it will go true for one k-rate cycle on every rising edge received (as the name implies)?

a|x


#10

In my todo-list :smile:


Eventually, yes, but that will become a different inlet type to maintain compatibility.


#12

This seems to work.

   <obj.normal id="tbnmpgen" uuid="f4aa3eb163415b6fc722e576dde754a226a022ac">
      <sDescription>Pseudo-random (repeatable) pattern generator. Based on Nord modular PatternGen module.</sDescription>
      <author>toneburst</author>
      <license>BSD</license>
      <inlets>
         <bool32.rising name="trig" description="trigger"/>
         <bool32.rising name="r" description="reset"/>
      </inlets>
      <outlets>
         <frac32.bipolar name="out"/>
      </outlets>
      <displays/>
      <params>
         <frac32.u.map name="bankindex" description="Bank"/>
         <frac32.u.map name="patternindex" description="Pattern"/>
      </params>
      <attribs>
         <spinner name="length" description="Pattern Length" MinValue="0" MaxValue="128" DefaultValue="16"/>
      </attribs>
      <code.declaration><![CDATA[int32_t pattern[128] = {};
int32_t counter;
int ntrig;
int rtrig;
int32_t bindex;
int32_t pindex;
int32_t outval;
int init = 1;

int32_t rstate = 1;

// Seeded random number generator from:
// http://blog.embedded-office.com/en/blog-artikel/items/random-1.html
// (cryptographically rubbish, but should be fine for our porpoises)
int32_t updatestate() {
	int32_t a = 5;
	int32_t b = 12345;
	int32_t m = 511;
	rstate = ((rstate * a + b) % m) - 255;
	return rstate;
}

// Update pattern array
void newpattern(int32_t a, int32_t b, int len) {
	rstate = a * 64 + b;
	for(int i = 0; i < len; i++) {
		pattern[i] = updatestate();
	}
}
]]></code.declaration>
      <code.init><![CDATA[counter = 0;
ntrig = 0;
rtrig = 0;]]></code.init>
      <code.krate><![CDATA[// Looprunning for 1st time
if (init) {
	newpattern(param_bankindex, param_patternindex, attr_length);
	init = 0;
}

// Trigger input rising edge
if ((inlet_trig>0) && !ntrig) {
    	// Check for changes to parameters
    	// and regenerate array if params changed
	if ((param_bankindex != bindex) || (param_patternindex != pindex)) {
		newpattern(param_bankindex, param_patternindex, attr_length);
		bindex = param_bankindex;
		pindex = param_patternindex;
	}
    // Update/reset counter
    counter = (counter>attr_length)? 0 : counter + 1;
    // Set output value until next trigger
    outval = pattern[counter];
    ntrig=1;
} else if (!(inlet_trig>0)) {
    ntrig=0;
}

// Reset input rising edge
if ((inlet_r>0) && !rtrig) {
    rtrig = 1;
    counter = 0;
} else if (!(inlet_r>0)) {
    rtrig=0;
}

outlet_out = outval<<19;]]></code.krate>
   </obj.normal>
</objdefs>

Any tips on making it better gratefully received. Particularly in terms of variable types etc.. I've just ended up making everything int32_ts.. no idea if this is the best way or not...

a|x


#13

Tidied up the code a little, renamed and added descriptions for in/outlets.

a|x


#14

I'm moving this to the Axolotl Contrib library on GitHub.

a|x