## 6 Multithreading Support
### Standard Algorithms  
Another example shows the use of standard algorithms in spectral processing. The opcode in Listing 16 implements _spectral tracing_[29], which retains only a given number of bins in each frame, according to their amplitude. To select the bins, we need to sort them to find out the ones we want to retain (the loudest \(N\)). For this we collect all amplitudes from the frame and then apply _nth element_ sorting, placing the threshold amplitude in element \(n\). Then we just filter the original frame according to this threshold. Here we have the performance code (amps is a dynamically allocated array belonging to the Plugin object). ``` structPVTrace:csnd::FPlugin<1,2>{ csnd::AuxMem<float>amps; staticconstexprcharconst*otypes=\"f\"; staticconstexprcharconst*ity=\"fk\"; intinit(){ if(inargs.fsig_data(0).isSliding()) returncsound->init_error(Str(\"slidingnotsupported\"));if (inargs.fsig_data(0).fsig_format()!=csnd::fsig_format::pvs &&  inargs.fsig_data(0).fsig_format()!=csnd::fsig_format::polar)  return csound->init_error(Str(\"fsig format not supported\"));

amps.allocate(csound, inargs.fsig_data(0).nbins());  csnd::Fsig &fout = outargs.fsig_data(0);  fout.init(csound, inargs.fsig_data(0));  framecount = 0;  return OK;  }

int kperf() {  csnd::pv_frame &fin = inargs.fsig_data(0);  csnd::pv_frame &fout = outargs.fsig_data(0);

if (framecount < fin.count()) {  int n = fin.len() - (int) inargs[1];  float thrsh;  std::transform(fin.begin(), fin.end(), amps.begin(),  [](csnd::pv_bin f) { return f.amp(); });  std::nth_element(amps.begin(), amps.begin() + n, amps.end());  thrsh = amps[n];  std::transform(fin.begin(), fin.end(), fout.begin(),  [thrsh](csnd::pv_bin f) {  return f.amp() >= thrsh? f : csnd::pv_bin();  });  framecount = fout.count(fin.count());  }  return OK;  };

### Discussion

One of the main objectives of this work, as stated earlier, is to provide support for more modern C++ approaches to opcode development, which can, in particular, facilitate the writing of signal processing code. Under this point, we were able to show through the comparison of CPOF and C API code that such objective has been met. A cursory comparison of the two versions demonstrates a number of surface differences in the CPOF version: economy of expressions, code length (36 lines vs. 59), and clarity of context. A closer look will reveal how the semantics of the process (the application of a filter equation) is far better realised in the C++ version, without the intrusion of set-up and boilerplate code. In fact, this comparative exercise points out to the fact that a lot of code re-use could actually replace the traditional approach to write components such as filters. We were able to demonstrate how this is possible in a simple case of stateless operators, but the principle is applicable to other types of opcodes, of varying levels of complexity. In the case of filters, by supplying different update() and filter lambdas to a template class, a whole family of such processors can be implemented. Beyond this extensive code re-use, we can also make avail of standard algorithms for these tasks, which not only simplifies some of the development stages, but also helps improve correctness of implementation. We may conclude that the availability of CPOF in the Csound code allows for better engineering practices in the development of opcodes. A corollary of this conclusion is that CPOF provides Csound a much more advanced means of UG development than what is available in similar music programming environments. This is because, as we have noted in Section 1.2, a more traditional C-like OOP idiom is prevalent elsewhere (even where C++ is the implementation language). Another important result of this work, which should be highlighted here is the integration of modern C++ practices into a pure-C environment. While this has been done elsewhere, the extent to which we were able to apply the C++11 standard and generate a completely C-compatible code is of note.