Channel problem with cubase se 1.0


#1

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


#2

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!


#3

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


#4

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.


#5

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; }


#6

Ok, I’ll figure something out…


#7

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


#8

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]


#9

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


#10

Oh, right.