## 6 Multithreading Support
(*p->istor)))  p->yt1=0.0;  returnOK;int tone(CSOUND *csound, TONE *p) {  MYFLT *ar, *asig;  uint32_t offset = p->h.insdshead->ksmps_offset;  uint32_t early = p->h.insdshead->ksmps_no_end;  uint32_t n, nsmps = CS_KSMPS;  double cl = p->cl, c2 = p->c2;  double yt1 = p->ytl;  
if (*p->khp!= (MYFLT)p->prvhp) {  double b;  p->prvhp = (double)*p->khp;  b = 2.0 - cos((double)(p->prvhp * TWOPI/csound->GetSr()));  p->c2 = c2 = b - sqrt(b * b - 1.0);  p->cl = c1 = 1.0 - c2;  }  ar = p->ar;  asig = p->asig;  if (UNLIKELY(offset)) memset(ar, '\0', offset*sizeof(MYFLT));  if (UNLIKELY(early)) {  nsmps -= early;  memset(&ar[nsmps], '\0', early*sizeof(MYFLT));  }  for (n=offset; n<nsmps; n++) {  yt1 = cl * (double) (asig[n]) + c2 * yt1;  ar[n] = (MYFLT)ytl;  }  p->ytl = yt1;  return OK; }  
int csoundModuleInit(CSOUND *csound) {  csoundAppendOpcode(csound, \"tone\", sizeof(TONE), 0, 5, \"a\", \"ako\",  (SUBR) toneset, NULL, (SUBR) tone);  return OK; };  
int csoundModuleCreate(CSOUND *csound) { return OK; };  int csoundModuleDestroy(CSOUND *csound) { return OK; };  
This code example follows the straight C API opcode implementation: a data structure is provided, along with init-time and audio-rate perf-time functions. The opcode is registered using csoundAppendOpcode() called in csoundModuleInit(). Looking at the code, besides the typical C idiom, we see that there are a number of lines of code devoted to saving to the dataspace, setting sample-accurate parameters (offset, nsmps), and so on. In contrast, the CPOF version, in Listing 13, is much more succinct. The functions have direct access to the dataspace, which allows them to update state directly. The use of iterators allow us to replace the loop with a single function call containing a lambda expression. The attention of the programmer is directed to the actual filter equation implemented therein. The code is made more compact also by replacing the filter update lines by inline calls to the update() method. All the boilerplate code present in the C version is hidden away by the framework. Finally, opcode registration is much simplified by the use of the function template. ```
structTone:csnd::Plugin<1,3>{staticconstexprcharconst*otypes=\"a\"; staticconstexprcharconst*itypes=\"aio\"; doublecl; doublec2; doubleytl; doubleprvhp; voidupdate(){ prvhp=(double)inargs[1]; doubleb=2.0-cos(prvhp*csnd::twopi/csound->sr()); c2=b-sqrt(b*b-1.0); c1=1.0-c2; }
```
```
intinit(){ update(); if(!inargs[2])ytl=0. ; returnOK; }
```  
```
intaperf(){ csnd::AudioSigin(this,inargs(0)); csnd::AudioSigout(this,outargs(0)); doubley=ytl; if(prvhp!=inargs[1])update(); std::transform(in.begin(),in.end(),out.begin(), [this,&y](MYFLTx){ return(y=cl*x+c2*y); }); yt1=y; returnOK; } }; voidcsnd::on_load(Csound*csound){ csnd::plugin<Tone>(csound,\"tone\",csnd::thread::ia); };
```  
While detailed performance considerations are beyond the scope of this paper, it is nonetheless useful to observe how the two versions of this simple opcode compare in that respect. Using standard timing tests, it was found that the elapsed CPU times of both versions are very close, as illustrated in Table 1 for various vector size (ksmps) values.