Does the SynthDef Binary File Format (.scsyndef files) contain redundant information?

I’m making a parser for .scsyndef files and noticed some weird things in the spec particularly in the bit dealing with parameters. The spec says

  • int32 - number of parameters (P)
  • [float32] * P - initial parameter values
  • int32 - number of parameter names (N)
  • [ param-name ] * N
    • pstring - the name of the parameter
    • int32 - its index in the parameter array

which ends up looking something like

const numInitialValues = int32();
let initialValues = [];

for (let i = 0; i < numInitialValues; i++) {
    initialValues.push(float32());
}


const numParamNames = int32();
let paramNames = [];

for (let i = 0; i < numParamNames; i++) {
    const name = pstring();    
    const index = int32();

    paramNames[index] = name;
}

// Merge names and values arrays
// ...

(Simplified version of my actual code)

This confuses me a lot. Why is the number of parameter names stored seperately from the number of initial values? Why do the parameter names have an index specified when the values don’t? How come initial values come first then the parameter names?

I know these are highly specific questions and probably difficult to answer so any thoughts or pointers would be greatly appreciated.

Thanks!

Because a single parameter name may comprise multiple values – arrayed parameters. You would be correct if there were always a one-to-one relationship between a parameter name and a value – but that isn’t true.

(
b = SynthDef(\test, { |abc = 1, def = #[2, 3, 4], ghi = 5|
	var array = NamedControl.kr(\jkl, Array.fill(5, { |i| i + 6 }));
	Out.ar(0, DC.ar(0))
}).asBytes;
)

b[0..3]  // SCgf
b[4..7]  // ver 2
b[8..9]  // 1 def

b[10..14]  // "test"
b[15..18]  // 1 constant
b[19..22]  // 0.0

b[23..26]  // 10 params

b[27 .. 27 + (10*4) - 1].clump(4).collect { |row|
	var b32 = 0;
	row.do { |byte| b32 = b32 << 8 | (byte % 256) };
	Float.from32Bits(b32)
};
-> [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]  // correct!

b[67..70]  // 4 param names

b[71..74]  // "abc"
b[75..78]  // 0 = index

b[79..82]  // "def"
b[83..86]  // 1 = index

b[87..90]  // "ghi"
b[91..94]  // 4 = index -- i.e. NOT consecutive

I’ll stop there, but you see that the first parameter (abc) has one value, so the index for the next parameter name (def) increments by 1. The second parameter has three values, so the index for ghi increments by 3.

This is used to protect against array overflow in /n_set.

hjh

1 Like

Thanks for the helpful detailed post!