## 5 Implementation
### Cosound Opcodes
#### 5.4.2 tvconv  
The tvconv opcode takes two input signals and implements time-varying convolution. We can nominally take one of these signals as the impulse response and the other as the input signal, but, in practice, no such distinction is made. The opcode takes the length of the filter and its partitions as parameters, and includes switches to optionally fix coefficients instead of updating them continuously: asig tvconv ain1, ain2, xupdate1, xupdate2, ipartsize, ifilsize, where its parameters are as follows:* ainl,ain2: input signals. * xupdate1,xupdate2: update switches \(u\) for each input signal. If \(u=0\), there is no update from the respective input signal, thus fixing the filter coefficients. If \(u>0\), the input signal updates the filter as normal. This parameter can be driven from an audio signal, which would work on a sample-by-sample basis, from a control signal, which would work on a block of samples at a time (depending on the ksmps system parameter, the block size), or it can be a constant. Each input signal can be independently _frozen_ using this parameter. * ipartsize: partition size, an integer \(P,0<P\leq N\), where \(N\) is the filter size. For values \(P>1\), the actual partition size will be quantised to \(Q=2^{k}\), \(k\in\mathbb{Z}\), \(Q\leq P\). * iflisize: filter size, an integer \(N\), \(N\geq P\), where \(P\) is the partition size. For partition size values \(P>1\), the actual filter size will be quantised to \(O=2^{k}\), \(k\in\mathbb{Z}\), \(O\leq N\). This opcode is programmed in C++ using the Csound Plugin Opcode Framework (CPOF) [45; 46], as the TVConv class. In this code, there are, in fact, two implementations of the process, which are employed according to the partition size:  
1. For partition size = 1: direct convolution in the time domain is used, and any filter size is allowed. The following method in TVConv implements this (listing 1. The vectors in and ir hold the two delay lines, which take their inputs from the signals in inp and irp. The variables frzl and frz2 are signals that control the freezing/updating operation for each input. ```
intdconv(){ csnd::AudioSiginsig(this,inargs(0)); csnd::AudioSigirsig(this,inargs(1)); csnd::AudioSigoutsig(this,outargs(0)); autoirp = irsig.begin(); autoinp = insig.begin(); autofrzl = inargs(2); autofrz2 = inargs(3); autoinc1 = csound->is_asig(frzl); autoinc2 = csound->is_asig(frz2); for(auto &s : outsig) { if(*frzl > 0) *itn = *inp; if(*frz2 > 0) *itr = *irp; itn++, itr++; if(itn == in.end()) { itn = in.begin(); itr = ir.begin(); } } s = 0.; for(csnd::AuxMem<MYFLT>::iteratoritl = itn, it2 = ir.end() - 1; it2 >= ir.begin(); itl++, it2--) { if(itl == in.end()) itl = in.begin(); s += *itl * it2; } frzl += incl, frz2 += inc2; inp++, irp++; }
```return OK; }
2. For partition size \(>1\), partitioned convolution is used (listing 2), through an overlap-add algorithm. In this case, the process is implemented in the spectral domain, and in order to make it as efficient as possible, power-of-two partition and filter sizes are enforced internally. ``` intpconv(){ csnd::AudioSiginsig(this,inargs(0)); csnd::AudioSigirsig(this,inargs(1)); csnd::AudioSigoutsig(this,outargs(0)); autoirp=irsig.begin(); autoinp=insig.begin(); auto*frz1=inargs(2); auto*frz2=inargs(3); autoinc1=csound->is_asig(frz1); autoinc2=csound->is_asig(frz2); for(auto&s:outsig){ if(*frz1>0)itn[n]=*inp; if(*frz2>0)itr[n]=*irp; s=out[n]+saved[n]; saved[n]=out[n+pars]; if(++n==pars){ cmplx*ins,*irs,*ous=to_cmplx(out.data()); std::copy(itn,itn+ffs,itusp); std::copy(itr,itr+ffs,itresp); std::fill(out.begin(),out.end(),0.