As you need a dynamic array later it's probably better to just introduce it here already and make a copy right away.
Done.
I'd suggest to use flexible array member for `mods`, and variable size struct for the `sf_modulators`
This would require either a double pointer indirection or returning the new pointer from `copy_modulators`/`add_modulator`, both of which seem to be less readable than the current version. Or am I missing something?
it would make cleaning up as simple as `free(modulators)` instead of having to check `capacity` as a hint to whether it has been dynamically allocated or not.
It's now `free(modulators.mods)` which is close.