This is Direct Form 1 but it's still cascaded SOS. it's in C, but you should be able to translate it to Python easily. the register
keyword is not important.
typedef struct
{
float filterCoefficients[5]; /* b0, b1, b2, a1, a2 (a0 is normalized to 1) */
} sectionData;
void filterBuffer(int32 Nsamples, int Nsections, float* y, float* x, float* filterStates, sectionData** sections)
{
sectionData** section_ptr;
float sampleValue, state1, state2, *state_ptr, *coef_ptr;
for (int n=0; n<Nsamples; n++)
{
section_ptr = sections; // reset section pointer
state_ptr = filterStates; // reset state pointer
sampleValue = x[n]; // get input sample
state2 = *state_ptr++; // x[n-2]
state1 = *state_ptr--; // x[n-1]
*state_ptr++ = state1; // x[n-1] -> x[n-2]
*state_ptr++ = sampleValue; // x[n] -> x[n-1]
int i = Nsections;
while (--i >= 0)
{
coef_ptr = &((*section_ptr++)->filterCoefficients[0]); // point to section filter coefficients
sampleValue = *coef_ptr++ * sampleValue; // b0*x[n]
sampleValue += *coef_ptr++ * state1; // b1*x[n-1]
sampleValue += *coef_ptr++ * state2; // b2*x[n-2]
state2 = *state_ptr++; // y[n-2]
state1 = *state_ptr--; // y[n-1]
*state_ptr++ = state1; // y[n-1] -> y[n-2]
sampleValue += *coef_ptr++ * state1; // -a1*y[n-1]
sampleValue += *coef_ptr++ * state2; // -a2*y[n-2]
*state_ptr++ = sampleValue; // y[n] -> y[n-1]
// at this point sampleValue is the output of the section just completed
// and will be the input to the section about to be computed unless this
// is the last section, in which case sampleValue is the output.
// state1 and state2 are the output states of the section just completed
// and will be the input states of of the section about to be computed.
}
y[n] = sampleValue; // store output sample
}
}