Working with a Csound inside a host’s audio thread – c++

Hi Csounders!

What would be the right way to read the samples from host’s audio callback IO buffer into a Csound object, process them and write them back, but with different sizes of ksmps and host’s IO buffer?

I.e. if a host’s audio callback has an i.e. 512 frame samples and Csound object has 256 ksmps.
The most logical way would be to simply make two iterations of usual steps:

  1. read ksmps number of values from host’s input buffer and write them into Csound Spin buffer
  2. make PerformKsmps()
  3. read result values from Csound Spout buffer and write them to the host’s output buffer

However, that doesn’t work as expected, for some reason I get corrupted audio.

On the other hand, if ksmps size matches host’s IO buffer then everything works perfectly (of course in this case it involves only one iteration through the above steps).

What am I missing here?

Hi @Lovre The steps you outline should work fine. Here is what I typically do with the JUCE process routine. Note that Csound buffer, and the host buffer uses independent sample positions. This ensures everything works fine even if the host has a different buffer size to Csound’s ksmps.


float** ioBuffer = buffer.getArrayOfWritePointers();
const int channelNum = buffer.getNumChannels();

for (int i = 0; i < numSamples; i++, ++csndIndex)
{
    if (csndIndex >= csdKsmps)
    {
        csound->PerformKsmps();
        csndIndex = 0;
    }
    
    pos = csndIndex * channelNum;
    for (int channel = 0; channel < getTotalNumOutputChannels(); channel++)
    {
        ioBuffer[channel][i] =  CSspout[csndPosition] / cs_scale;
        pos++;
    }
}

Hi @rory! Yes, the steps I described above work fine, I made a rooky pointer mistake :triumph:
Nevertheless, I took your code because it is more elegant than mine and I modified it a bit for my use case:

void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill){
    MYFLT* CSspout = _csound->GetSpout();
    MYFLT* CSspin = _csound->GetSpin();

    int numSamples = bufferToFill.numSamples;
    int numChannels = bufferToFill.buffer->getNumChannels();

    const float** ioBuffer_rd = bufferToFill.buffer->getArrayOfReadPointers();
    float** ioBuffer_wr = bufferToFill.buffer->getArrayOfWritePointers();
    int csndPosition = 0;
    
    for (int sample = 0; sample < numSamples; sample++)
    {
        csndPosition = _csndIndex * numChannels;
        // Step 1) write new sample frame into CSOUND Spin
        for (int channel = 0; channel < numChannels; channel++)
            CSspin[csndPosition + channel] = ioBuffer_rd[channel][sample] * cs_scale;

        // Step 2) if csdKsmps frame samples are written in CSOUND Spin -> PerformKsmps
        if (_csndIndex >= csdKsmps - 1)
        {
            _csound->PerformKsmps();
            _csndIndex = 0;
        }
        else _csndIndex++;

        csndPosition = _csndIndex * numChannels;
        // Step 3) read result frames from CSOUND Spout
        for (int channel = 0; channel < numChannels; channel++)
            ioBuffer_wr[channel][sample] = CSspout[csndPosition+channel] / cs_scale;
    }
}

Thank you very much for your help!

1 Like