Channel problem with cubase se 1.0

Hi Jules,

I just ran into a kind of bug with cubase se 1.0 (version from 2004).

My plugin has 0 input and 2 output channels.
What happens is that during the first few calls to processReplacing, cubase is sending the same output buffer for the two channels (outputs[0] == outputs[1]), and then suddenly it starts to feed it with two different buffers (outputs[0] != outputs[1]). It happens without any call to suspend / resume or anything else. As the tempChannels stuff is initialized only once (on the first call a temporary buffer is created for channel 1), it is not updated when cubase starts behaving correctly and one only hears the left channel.

As I don’t have any input channel I have just disabled the tempChannels stuff, but I think a more robust approach would be to reupdate the tempChannels at each call to processreplacing

Jeez, what a PITA…

The logic it uses to set up those temp channels is too heavy for it to be run every time the callback happens… I’m a bit stuck as to how to do a decent workaround for this. Suggestions welcome!

Well I would check it during each process (provided that the allocation / deallocation of new channels is still an extremely rare event that may happens during the first few calls), for example:

[code] //if (! hasCreatedTempChannels)
//{
// do this just once when we start processing…
hasCreatedTempChannels = true;

                // if some output channels are disabled, some hosts supply the same buffer
                // for multiple channels - this buggers up our method of copying the
                // inputs over the outputs, so we need to create unique temp buffers in this case..
                for (int i = 0; i < numOut; ++i)
                {
                    for (int j = i; --j >= 0;)
                    {
                        if (outputs[j] == outputs[i] && outputs[i] != 0)
                        {
                          if (!tempChannels[i])
                            tempChannels.set (i, juce_malloc (sizeof (float) * blockSize * 2));
                          break;
                        } else if (tempChannels[i]) {
                          juce_free(tempChannels.getUnchecked(i));
                          tempChannels.set(i, 0);
                        }
                    }
                }
                //}

[/code]

Given the typical number of channels, the cost should be a few nanoseconds in my opinion so it’s not even worth avoiding the second for loop

It’s still a waste of processor time, and it’s an n*n time algorithm, so as the channel count goes up it could turn into a problem. The only other solution I can think of is to just always use temp buffers and copy them into the destination, but that’s also wasteful.

if that is the O(n²) which is bothering you, you can use a two pass loop:

for (int i = 0; i < numOut; ++i) {
if (output[i] == 0 || outputs[i][0] != 0) { // need a temp channel
… stuff …
} else {
outputs[i][0] = 1; // flag the buffer
}
}

// clear the flags
for (int i = 0; i < numOut; ++i) { outputs[i][0] = 0; }

Ok, I’ll figure something out…

Can’t you just reset it on the resume call?

Ok, tell me if this logic looks right to you guys… I’ve just made it check each time, but optimised it by rolling the check into the loop that follows it:

[code] if (filter->isSuspended())
{
for (int i = 0; i < numOut; ++i)
zeromem (outputs[i], sizeof (float) * numSamples);
}
else
{
int i;
for (i = 0; i < numOut; ++i)
{
float* chan = (float*) tempChannels.getUnchecked(i);

                if (chan == 0)
                {
                    chan = outputs[i];

                    // if some output channels are disabled, some hosts supply the same buffer
                    // for multiple channels - this buggers up our method of copying the
                    // inputs over the outputs, so we need to create unique temp buffers in this case..
                    for (int j = i; --j >= 0;)
                    {
                        if (outputs[j] == chan)
                        {
                            chan = juce_malloc (sizeof (float) * blockSize * 2);
                            tempChannels.set (i, chan);
                            break;
                        }
                    }
                }

                if (i < numIn && chan != inputs[i])
                    memcpy (chan, inputs[i], sizeof (float) * numSamples);

                channels[i] = chan;
            }

            for (; i < numIn; ++i)
                channels[i] = inputs[i];

            AudioSampleBuffer chans (channels, jmax (numIn, numOut), numSamples);

            filter->processBlock (chans, midiEvents);
        }

[/code]

Jules: it looks perfect to me !

Rock: the problem is with buggy hosts such as my version of cubase that does not even call resume before changing its buffers

Oh, right.