Plugin Multichannel Output

I’m more than a bit confused about how to deal with writing to multiple channels in a UGen. SC_Plugin.hpp seems to imply that you can use out(i) to write to the ith output such that out(0) is the first output, out(1) is the second output and so on.

However, if I try to modify an input signal for each output, the operation takes place on the first output out(0) and then that seems to get used by each subsequent operation as it’s input. Here is a modified version of SimpleGain that shows what I mean.

void SimpleGain::next(int nSamples) {
  auto* unit = this;
  const float* input = in(1); // input signal is the second input

  // simple gain function
  for(int c=0; c<numChans; c++) {
    float* outbuf = out(c); // get the cth output
    // const float* input = in(1); // input signal is the second input

    for (int i = 0; i < nSamples; ++i) {
      if(c==0) {
        outbuf[i] = 1; // make channel 0 DC
      } else {
        outbuf[i] = input[i]; // output the input
      }
    } // end for (nSamples)
  } // end for (numChans)
}

Despite trying to assign the input signal to every channel that is not the first, the result from the first operation is repeated on each subsequent channel. I’ve tried all sorts of variations but don’t know enough about the SC API to get anything different. This is the output on the scope:

What am I doing wrong?

It appears that the first audio output overwrites the first audio input. Iterating over the channels backwards works:

// go backwards
for(int c=numChans-1; c>=0; c--) {
  float* outbuf = out(c); // get the cth output

  for (int i=0; i<nSamples; i++) {
    if(c==0) {
      outbuf[i] = 1; // make channel 0 DC
    } else {
      outbuf[i] = input[i]; // output the input
    }
  } // end for (nSamples)
} // end for (numChans)

Also copying the input into a buffer before manipulating works as well but uses memory unnecessarily.

Can anyone chime in as to why this is so? Nothing like this is mentioned anywhere in the documentation and it seems pretty critical to being able to effectively write UGens.

What does your plugin load look like? You may need to use the can’t alias macros.

Josh

Plugin Load:

PluginLoad(SimpleGainUGens) {
  // Plugin magic
  ft = inTable;
  registerUnit<SimpleGain::SimpleGain>(ft, "SimpleGain");
}

Constructor:

SimpleGain::SimpleGain() {
  auto* unit = this;
  mCalcFunc = make_calc_function<SimpleGain, &SimpleGain::next>();
  numChans = (int) in0(0); // this line doesn't affect the output
  std::cout << numChans << "\n";
  next(1);
}

What are can’t alias macros?

Not sure how this works with CookieCutter - but in my Ambisonic UGens in sc3-plugins (that output multiple outputs and shouldn’t touch the input) I use this in the load:

PluginLoad(JoshAmbi)

{

ft = inTable;

DefineSimpleCantAliasUnit(BFEncode1);

DefineSimpleCantAliasUnit(BFEncode2);

DefineSimpleCantAliasUnit(BFEncodeSter);

DefineSimpleCantAliasUnit(FMHEncode0);

DefineSimpleCantAliasUnit(FMHEncode1);

DefineSimpleCantAliasUnit(FMHEncode2);

DefineSimpleCantAliasUnit(BFDecode1);

DefineSimpleCantAliasUnit(FMHDecode1);

DefineSimpleUnit(BFManipulate);

DefineSimpleCantAliasUnit(B2Ster);

DefineSimpleUnit(A2B);

DefineSimpleUnit(B2A);

DefineSimpleCantAliasUnit(UHJ2B);

DefineSimpleCantAliasUnit(B2UHJ);

}

Buffer Aliasing will try to reuse pointers from input and output - DefineSimpleCantAliasUnit avoids that. You’d need to see what that expands to in the regular SC Source.

Best.

Josh

Ah, ok, thanks. I’ll test it out this week, hopefully… As it stands it seems to be working. I won’t break it tonight.

Just an update:

DefineSimpleCantAliasUnit doesn’t work with the cookie cutter templates since there is no function named UNITNAME_Ctor. I tried to modify it but I don’t really know what I’m doing and the solution of writing the outputs backwards seems to work for now.

[edited for grammar]