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]

hey @woolgathering, sorry for the late reply and hope you haven’t abandoned this yet.

the cookie cutter template, which I designed, had the initial goal of making it easy to jumpstart a clean plugin project with the C++ plugin API, taking care of all the annoying boilerplate like CMake and integration with Travis and Appveyor. the C++ plugin API is relatively fresh, and not many plugins that i’m aware of use it. although we are trying to actively encourage its use, it’s not even used in any of the plugins in the main SC repo. so, it’s not surprising that you are running into issues.

i think you have been posting recently about some of those problems – i was wondering if you wouldn’t mind putting together a list of the parts of the API you’ve found difficult to work with? so far i have seen you posting about GET_BUF and DefineSimpleCantAliasUnit. i can sit down at some point before the end of the year and try to make those parts better integrated. trying to ‘solve’ the entire API would probably be overwhelming in its current state, so it would be super helpful to know what things to focus on first. might also be instructive for me to try to write some non-trivial plugins with it myself. :slight_smile:

@VIRTUALDOG Haven’t abandoned it and the project is almost complete! I’ll post about when it’s worthy of being publicly released. And sorry I haven’t submitted a patch for the AppVeyor issue on the cookiecutter yet; haven’t gotten around to it but it’s easy so hopefully this weekend (Holidays? Days off? Never heard of it.)

Sure, I can develop a list, at least of things I’ve encountered. Would it be more helpful to post that here or on the cookiecutter repo or…?

great!

i was going to look into the appveyor issue myself soon, so don’t worry about that.

could you please just leave the list here? thanks :slight_smile:

A tentative list (will be appended):

  • GET_BUF. Really all the info on how to reliably get a buffer. GET_BUF is great but then you have to make the first input the buffer and sometimes that’s not ideal.
  • DefineSimpleCantAliasUnit. Probably all those macros. Pointer aliasing needs to be made explicitly clear.
  • RTAlloc. How to work with realtime memory allocation and dealloctaion. The only info out there is in Daniel Nouri’s online archive of the docs for 3.2!
  • I’d also be very interested in knowing how to write FFT UGens. I’m particularly interested in implementing Ted Apel’s Feature Preservation Phase Vocoder as a superior FFT analysis paradigm.
2 Likes