Supporting an Object-Oriented Approach to Unit Generator Development: The Csound Plugin Opcode Framework †
Abstract
:1. Introduction
1.1. Csound Unit Generators
- An interface for registering new opcodes, provided by Csound API.
- Dynamic library loading is provided by the audio engine at startup. As part of this, a given directory is searched for suitable library files containing new opcodes.
- initialisation time: code is executed once, after instantiation. It can also be invoked again if a re-initialisation pass is requested.
- performance time: code is executed repeatedly each control cycle to consume and/or produce a signal. There are two separate types of functions that can be defined for performance:
- (a)
- control: this function is invoked to deal with scalar inputs or outputs (e.g., processing a single sample at a time).
- (b)
- audio: code is expected to deal with audio signals, which are passed as vectors to it.
- i-type: these variables are floating-point scalars that can only change at initialisation or re-initialisation time.
- k-type: also floating-point scalars, these will only change at performance time, although they can also be initialised at i-time.
- a-type: this is a floating-point vector, which is modified at performance time. The length of this vector is defined by a ksmps variable that can assume local (instrument) values or can be set globally for the whole engine. Vectors can also be initialised at i-time. Audio-rate functions are designed to operate on these variables.
- S-type: character strings, which can be modified at i-time and perf-time, although it is more common to do so only at i-time.
- f-type: frequency-domain signals (fsigs), these contain self-describing spectral data (of different formats) that are processed at performance-time by control-rate functions.
- arrays: composite-type variables of each of the above types. A very important case is k and i arrays, for which there are various important applications.
1.1.1. Opcode Layout
- A data structure declared with the following format. It always contains an OPDS member as its first component. This holds a set of elements common to all opcodes.
struct NAME { OPDS h; // output argument addresses // input argument addresses // dataspace members };
where we need to declare one pointer variable for each output and input argument (in the order they should occur in the Csound language code). When the opcode is called, the output and input argument addresses are passed to Csound through these pointers. The C variable types for different Csound argument types are:- –
-
MYFLT∗: pointers to the internal floating-point data type (MYFLT is either defined as a 64 or a 32-bit float, depending on the engine version and platform) are used for all basic numeric arguments. (i, k, or a).
- –
-
STRINGDAT∗: used for string arguments.
- –
-
PVSDAT∗: holds an fsig argument
- –
-
ARRAYDAT∗: for array arguments (of any fundamental type).
- A function with the signature
int func(CSOUND ∗, void ∗);
for each one of the required action times (init, control, and/or audio). The first argument is a pointer to the Csound engine that instantiated this opcode. The second argument receives a pointer to the allocated dataspace for a given instance of the opcode.
int csoundAppendOpcode(CSOUND ∗csound, const char ∗opname, int dsblksiz, int flags, int thread, const char ∗outypes, const char ∗intypes, int (∗iopadr)(CSOUND ∗, void ∗), int (∗kopadr)(CSOUND ∗, void ∗), int (∗aopadr)(CSOUND ∗, void ∗));
1.1.2. Plugin libraries
csoundModuleCreate(CSOUND ∗csound); csoundModuleInit(CSOUND ∗csound); csoundModuleDestroy(CSOUND ∗csound);
PUBLIC int csoundModuleInit(CSOUND ∗csound) { csound->AppendOpcode(csound, "test", sizeof(struct OPTEST), 0, 1, "i", "i", test_init, NULL, NULL); return 0; }registers an opcode with the name test, running at init-time only, implemented by the function test_init. This function invokes the exact same code as the Csound API call, but it is more convenient for the purposes of a plugin library.
1.1.3. Discussion
1.2. Unit Generators and Plugins in Other Systems
2. The Framework
2.1. Design Fundamentals
- The main Csound engine is written in C. As we have noted, it instantiates opcodes and makes calls to opcode processing functions, but it does not support any C++ facilities.
- The process of registering an opcode with the engine requires that processing functions are defined as static. As we have seen, up to three different functions should be registered (for different action times).
- In C, the sub-classing of the base structure (OPDS) is achieved by adding this as its first member variable.
struct OPCD { OPDS h; };is binary equivalent to
struct OPCD : OPDS { };
struct Plugin : OPDS { // dataspace ... // stub methods int init() { return OK; } int kperf() { return OK; } int aperf() { return OK; } };from which we would inherit our own class to implement the new opcode:
struct MyClass: Plugin { ... };
template <typename T> int init(CSOUND ∗csound, T ∗p) { p->csound = (Csound ∗)csound; return p->init(); } template <typename T> int kperf(CSOUND ∗csound, T ∗p) { p->csound = (Csound ∗)csound; return p->kperf(); } template <typename T> int aperf(CSOUND ∗csound, T ∗p) { p->csound = (Csound ∗)csound; p->sa_offset(); return p->aperf(); }
template <typename T> int plugin(CSOUND ∗cs, const char ∗name, const char ∗oargs, const char ∗iargs, uint32_t thread, uint32_t flags = 0) { return cs->AppendOpcode(cs, (char ∗)name, sizeof(T), flags, thread, (char ∗)oargs, (char ∗)iargs, (SUBR)init<T>, (SUBR)kperf<T>, (SUBR)aperf<T>); }
Plugin<MyOpcode>(...);
2.2. Opcode Arguments
template <uint32_t N, uint32_t M> struct Plugin : OPDS { ... };where N and M will define how many outputs and inputs an opcode will take, respectively, which are defined by the class declaration (template instantiation).
template <uint32_t N> class Param { MYFLT ∗ptrs[N]; ... };
template <uint32_t N, uint32_t M> struct Plugin : OPDS { Param<N> outargs; Param<M> inargs; ... };
2.3. The Base Class
#include <plugin.h> struct MyPlug : csnd::Plugin<1,1> { };
- outargs: a Params object holding output arguments.
- inargs: input arguments (Params).
- csound: a pointer to the Csound engine object.
- offset: the starting position of an audio vector (for audio opcodes only).
- nsmps: the size of an audio vector (also for audio opcodes only).
- init(), kperf() and aperf() non-op methods, to be reimplemented as needed.
- out_count() and : these functions return the number of arguments for output and input, respectively. They are useful for opcodes with variable number of arguments.
- sa_offset(): this method calculates the correct values for offset and nsmps. User called does not need to invoke it, as it is called implicitly by the aperf() template function before it delegates to the plugin code.
2.3.1. Initialisation-time Opcodes
struct Simplei : csnd::Plugin<1,1> { int init() { outargs[0] = inargs[0]; return OK; } };
2.3.2. Control-rate Opcodes
struct Simplek : csnd::Plugin<1,1> { int kperf() { outargs[0] = inargs[0]; return OK; } };
2.3.3. Audio-Rate Opcodes
struct Simplea : csnd::Plugin<1,1> { int aperf() { std::copy(inargs(0)+offset, inargs(0)+nsmps, outargs(0)); return OK; } };
2.4. Registering Opcodes with Csound
template <typename T> int plugin(Csound ∗csound, const char ∗name, const char ∗oargs, const char ∗iargs, uint32_t thread, uint32_t flags = 0)where we have the following arguments:
-
csound: a pointer to the Csound object to which we want to register our opcode.
-
name: the opcode name as it will be used in Csound code.
-
oargs: a string containing the output argument types, one identifier per argument.
-
iargs: a string containing the input argument types, one identifier per argument.
-
thread: a code to tell Csound when the opcode should be active.
-
flags: multithread flags (generally 0 unless the opcode accesses global resources).
- thread::i: indicates init().
- thread::k: indicates kperf().
- thread::ik: indicates init() and kperf().
- thread::a: indicates aperf().
- thread::ia: indicates init() and aperf().
- thread::ika: indicates init(), kperf() and aperf().
#include <modload.h> void csnd::on_load(Csound ∗csound){ csnd::plugin<Simplei>(csound, "simple", "i", "i", csnd::thread::i); csnd::plugin<Simplek>(csound, "simple", "k", "k", csnd::thread::k); csnd::plugin<Simplea>(csound, "simple", "a", "a", csnd::thread::a); return CSOUND_OK; }
struct MyPlug : csnd::Plugin<1,2> { static constexpr char const ∗otypes = "k"; static constexpr char const ∗itypes = "ki"; ... };
template <typename T> int plugin(Csound ∗csound, const char ∗name, uint32_t thread, uint32_t flags = 0)
2.5. Constructing and Destroying Member Variables
template <typename T, typename ... Types> T ∗constr(T∗ p, Types ... args){ return new(p) T(args ...); }
A::A(int, float) { ... };
csnd::constr(&obj, 10, 10.f);where the arguments are the variable address, followed by any class constructor parameters. Again, given that the compiler knows that obj is of type A, it resolves the template without the need for an explicit type instantiation.
template<typename T> void destr(T ∗p) { p->T::~T(); }
int deinit() { csnd::destr(&obj); return OK; }
3. The Engine Object
- Messaging:
- –
- init_error(): takes a string message and signals an initialisation error.
- –
- perf_error(): takes a stringmessage, an instrument instance and signals a performance error.
- –
- warning(): warning messages.
- –
- message(): information messages.
- System parameters:
- –
- sr(): returns engine sampling rate.
- –
- _0dbfs(): returns max amplitude reference.
- –
- _A4(): returns A4 pitch reference.
- –
- nchnls(): return number of output channels for the engine.
- –
- nchnls_i(): same, for input channel numbers.
- –
- current_time_samples(): current engine time in samples.
- –
- current_time_seconds(): current engine time in seconds.
- –
- is_asig(): check for an audio signal argument.
- MIDI data access:
- –
- midi_channel(): midi channel assigned to this instrument.
- –
- midi_note_num(): midi note number (if the instrument was instantiated with a MIDI NOTE ON).
- –
- midi_note_vel(): same, for velocity.
- –
- midi_chn_aftertouch(): channel aftertouch.
- –
- midi_chn_polytouch(): polyphonic aftertouch.
- –
- midi_chn_ctl(): continuous control value.
- –
- midi_chn_pitchbend(): pitch bend data.
- –
- midi_chn_list(): list of active notes for this channel.
- FFT:
- –
- fft_setup(): FFT operation setup.
- –
- rfft(): real-to-complex, complex-to-real FFT.
- –
- fft(): complex-to-complex FFT.
- Memory allocation (Csound-managed heap):
- –
- malloc(): malloc-style memory allocation.
- –
- calloc(): calloc-style memory allocation.
- –
- realloc(): realloc-style memory allocation.
- –
- strdup(): string duplication.
- –
- free(): memory deallocation.
template <typename T> void plugin_deinit(T ∗p);
csound->plugin_deinit(this);
4. Toolkit Classes
4.1. Parameters
- operator[](): array-style access to numeric (scalar) parameter values.
- begin(), cbegin(): begin iterators for the parameter list.
- end(), cend(): end iterators.
- iterator and const_iterator: iterator types for this class.
- operator()(): function-style access to numeric (vector) parameter pointers.
- data(): same as the function operator, access to the parameter address.
- str_data(): access to parameter as a STRINGDAT reference (see Section 4.5).
- fsig_data(): access to parameter as a Fsig reference (fsig data class, see Section 5).
- vector_data(): access to parameter as a Vector<T> reference (Csound 1-D numeric array data, see Section 4.6).
- myfltvec_data(): access to parameter as a myfltvec reference (Csound 1-D numeric array, see Section 4.6).
4.2. Audio Signals
- operator[](): array-style access to individual samples.
- begin(), cbegin(): begin iterators for the audio vector.
- end(), cend(): end iterators.
- iterator and const_iterator: iterator types for this class.
- operator()(): function-style access to numeric (vector) parameter pointers.
AudioSig(OPDS ∗p, MYFLT ∗s, bool res = false);
struct Simplea : csnd::Plugin<1,1> { int aperf() { csnd::AudioSig in(this, inargs(0)); csnd::AudioSig out(this, outargs(0)); std::copy(in.begin(), in.end(), out.begin()); return OK; } };
4.3. Memory Allocation
- allocate(): allocates new memory whenever required.
- operator[]: array-subscript access to the allocated memory.
- data(): returns a pointer to the data.
- len(): returns the length of the vector.
- begin(), cbegin(): begin iterators to the data memory.
- end(), cend(): end iterators.
- iterator and const_iterator: iterator types for this class.
struct DelayLine : csnd::Plugin<1,3> { static constexpr char const ∗otypes = "a"; static constexpr char const ∗itypes = "aik"; csnd::AuxMem<MYFLT> delay; csnd::AuxMem<MYFLT>::iterator iter; int init() { delay.allocate(csound, csound->sr()∗inargs[1]); iter = delay.begin(); return OK; } int aperf() { csnd::AudioSig in(this, inargs(0)); csnd::AudioSig out(this, outargs(0)); MYFLT g = inargs[2]; std::transform(in.begin() ,in.end(), out.begin(), [this](MYFLT s) { MYFLT o = ∗iter; ∗iter = s + o∗g; if(++iter == delay.end()) iter = delay.begin(); return o; } ); return OK; } };
csnd::plugin<DelayLine>(csound, "delayline", csnd::thread::ia);
4.4. Function Table Access
- init(): initialises a table object from an opcode argument pointer.
- operator[]: array-subscript access to the function table.
- data(): returns a pointer to the function table data.
- len(): returns the length of the table (excluding guard point).
- begin(), cbegin(): iterators to the beginning of the function table.
- end(), cend(): end iterators.
- iterator and const_iterator: iterator types for this class.
struct Oscillator : csnd::Plugin<1,3> { static constexpr char const ∗otypes = "a"; static constexpr char const ∗itypes = "kki"; csnd::Table tab; double scl; double x; int init() { tab.init(csound,inargs(2)); scl = tab.len()/csound->sr(); x = 0; return OK; } int aperf() { csnd::AudioSig out(this, outargs(0)); MYFLT amp = inargs[0]; MYFLT si = inargs[1] ∗ scl; double ph = x; for(auto &s : out) { s = amp ∗ tab[(uint32_t) ph]; ph += si; while (ph < 0) ph += tab.len(); while (ph >= tab.len()) ph -= tab.len(); } x = ph; return OK; } };
csnd::plugin<Oscillator>(csound, "oscillator", csnd::thread::ia);
4.5. String Types
typedef struct { char ∗data; int size; } STRINGDAT;
struct Tprint : csnd::Plugin<0,1> { static constexpr char const ∗otypes = ""; static constexpr char const ∗itypes = "S"; int init() { char ∗s = inargs.str_data(0).data; csound->message(s); return OK; } };
csnd::plugin<Tprint>(csound, "tprint", csnd::thread::i);
4.6. Array Variables
- init(): initialises an output variable.
- operator[]: array-subscript access to the vector data.
- data(): returns a pointer to the vector data.
- len(): returns the length of the vector.
- begin(), cbegin(): iterators to the beginning and end of the vector.
- end(), cend(): end iterators.
- iterator and const_iterator: iterator types for this class.
- data_array(): returns a pointer to the vector data address.
struct SimpleArray : csnd::Plugin<1, 1>{ int init() { csnd::Vector<MYFLT> &out = outargs.vector_data<MYFLT>(0); csnd::Vector<MYFLT> &in = inargs.vector_data<MYFLT>(0); out.init(csound, in.len()); std::copy(in.begin(), in.end(), out.begin()); return OK; } int kperf() { csnd::Vector<MYFLT> &out = outargs.vector_data<MYFLT>(0); csnd::Vector<MYFLT> &in = inargs.vector_data<MYFLT>(0); std::copy(in.begin(), in.end(), out.begin()); return OK; } };
csnd::plugin<SimpleArray>(csound, "simple", "i[]", "i[]", csnd::thread::i);for i-time operation and
csnd::plugin<SimpleArray>(csound, "simple", "k[]", "k[]", csnd::thread::ik);for perf-time processing. This is an example of an overloaded opcode, that can operate on i and k-type variables. To facilitate the manipulation of this more common type of array, based on MYFLT, CPOF defines the following type:
typedef Vector<MYFLT> myfltvec;
5. Streaming Spectral Processing
typedef struct pvsdat { int32 N; int32 sliding; int32 NB; int32 overlap; int32 winsize; int32 wintype; int32 format; uint32 framecount; AUXCH frame; } PVSDAT;
- init(): initialises an fsig. There are two overloads: it is possible to initialise it from individual parameters (DFT size, hop size, etc.) or directly from another fsig. Initialisation is only needed for output variables.
- dft_size(): DFT size.
- hop_size(): hopsize.
- win_size(): window size.
- win_type(): returns the window type (Hamming = 0, von Hann =1, Kaiser = 2, custom = 3, Blackman = 4 and 5, Nutall = 6, Blackman-Harris = 7 and 8, rectangular = 9).
- nbins(): number of bins.
- count(): current frame count.
- isSliding(): checks for sliding mode.
- fsig_format(): returns the data format. This will vary depending on the source. To facilitate identification, CPOF defines the following constants:
- –
- fsig_format::pvs: standard phase vocoder frame, composed of bins containing amplitude and frequency pairs.There are bins (N is the DFT frame size), equally spaced between between 0 Hz and the Nyquist frequency (inclusive).
- –
- fsig_format::polar: similar to the pvs type, except that bins contain pairs of magnitude and phase data.
- –
- fsig_format::complex: as above, but with bins holding complex-format data (real, imaginary).
- –
- fsig_format::tracks: this format holds tracks of amplitude, frequency, phase, and track ID (used in partial tracking opcodes).
5.1. Phase Vocoder Data
- amp(): returns the bin amplitude.
- freq(): returns the bin frequency.
- amp(float a):: sets the bin amplitude to a.
- freq(float f): sets the bin frequency to f.
- operator∗(pv_bin f): multiply the amp of a pvs bin by f.amp.
- operator∗(MYFLT f): multiply the bin amp by f
- operator∗=(): unary versions of the above.
- operator[]: array-subscript access to the spectral frame
- data(): returns a pointer to the spectral frame data.
- len(): returns the length of the frame.
- begin(), cbegin() and end(), cend(): return iterators to the beginning and end of the data frame.
- iterator and const_iterator: iterator types for this class.
struct PVGain : csnd::FPlugin<1, 2> { static constexpr char const ∗otypes = "f"; static constexpr char const ∗itypes = "fk"; int init() { if(inargs.fsig_data(0).isSliding()) return csound->init_error("sliding not supported"); if(inargs.fsig_data(0).fsig_format() != csnd::fsig_format::pvs && inargs.fsig_data(0).fsig_format() != csnd::fsig_format::polar){ char ∗s = "format not supported"; return csound->init_error(s); } 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); uint32_t i; if(framecount < fin.count()) { std::transform(fin.begin(), fin.end(), fout.begin(), [this](csnd::pv_bin f){ return f ∗= inargs[1]; }); framecount = fout.count(fin.count()); } return OK; } };
csnd::plugin<PVGain>(csound, "pvg", csnd::thread::ik);
6. Multithreading Support
class PrintThread : public csnd::Thread { std::atomic_bool splock; std::atomic_bool on; std::string message; void lock() { bool tmp = false; while(!splock.compare_exchange_weak(tmp, true)) tmp = false; } void unlock() { splock = false; } uintptr_t run() { std::string old; while(on) { lock(); if(old.compare(message)) { csound->message(message.c_str()); old = message; } unlock(); } return 0; } public: PrintThread(csnd::Csound ∗csound) : Thread(csound), splock(false), on(true), message("") {}; ~PrintThread(){ on = false; join(); } void set_message(const char ∗m) { lock(); message = m; unlock(); } };
struct TPrint : csnd::Plugin<0, 1> { static constexpr char const ∗otypes = ""; static constexpr char const ∗itypes = "S"; PrintThread thread; int init() { csound->plugin_deinit(this); csnd::constr(&thread, csound); return OK; } int deinit() { csnd::destr(&thread); return OK; } int kperf() { thread.set_message(inargs.str_data(0).data); return OK; } };
7. Results
7.1. CPOF versus C API
typedef struct { OPDS h; MYFLT ∗ar, ∗asig, ∗khp, ∗stor; double c1, c2, yt1, prvhp; } TONE; int tonset(CSOUND ∗csound, TONE ∗p) { double b; p->prvhp = (double)∗p->khp; b = 2.0 - cos((double)(p->prvhp ∗ TWOPI/csound->GetSr())); p->c2 = b - sqrt(b ∗ b - 1.0); p->c1 = 1.0 - p->c2; if (LIKELY(!(∗p->istor))) p->yt1 = 0.0; return OK; } 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 c1 = p->c1, c2 = p->c2; double yt1 = p->yt1; 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->c1 = 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 = c1 ∗ (double)(asig[n]) + c2 ∗ yt1; ar[n] = (MYFLT)yt1; } p->yt1 = 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; };
struct Tone : csnd::Plugin <1, 3> { static constexpr char const ∗otypes = "a"; static constexpr char const ∗itypes = "aio"; double c1; double c2; double yt1; double prvhp; void update() { prvhp = (double) inargs[1]; double b = 2.0 - cos(prvhp∗csnd::twopi/csound->sr()); c2 = b - sqrt(b ∗ b - 1.0); c1 = 1.0 - c2; } int init() { update(); if (!inargs[2]) yt1 = 0.; return OK; } int aperf() { csnd::AudioSig in(this, inargs(0)); csnd::AudioSig out(this, outargs(0)); double y = yt1; if (prvhp != inargs[1]) update(); std::transform(in.begin(), in.end(), out.begin(), [this, &y](MYFLT x) { return (y = c1 ∗ x + c2 ∗ y); }); yt1 = y; return OK; } }; void csnd::on_load(Csound ∗csound) { csnd::plugin<Tone>(csound, "tone", csnd::thread::ia); };
7.2. Code Re-Use
template <MYFLT (∗op)(MYFLT)> struct ArrayOp : csnd::Plugin<1, 1> { int process(csnd::myfltvec &out, csnd::myfltvec &in) { std::transform(in.begin(), in.end(), out.begin(), [](MYFLT f) { return op(f); }); return OK; } int init() { csnd::myfltvec &out = outargs.myfltvec_data(0); csnd::myfltvec &in = inargs.myfltvec_data(0); out.init(csound, in.len()); return process(out, in); } int kperf() { return process(outargs.myfltvec_data(0), inargs.myfltvec_data(0)); } };
void csnd::on_load(Csound ∗csound) { csnd::plugin<ArrayOp<std::ceil>>(csound, "ceil", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::ceil>>(csound, "ceil", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::floor>>(csound, "floor", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::floor>>(csound, "floor", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::round>>(csound, "round", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::round>>(csound, "round", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::trunc>>(csound, "int", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::trunc>>(csound, "int", "k[]", "k[]", csnd::thread::i); csnd::plugin<ArrayOp<frac>>(csound, "frac", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<frac>>(csound, "frac", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::exp2>>(csound, "powoftwo", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::exp2>>(csound, "powoftwo", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::fabs>>(csound, "abs", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::fabs>>(csound, "abs", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::log10>>(csound, "log2", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::log10>>(csound, "log2", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::log10>>(csound, "log10", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::log10>>(csound, "log10", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::log>>(csound, "log", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::log>>(csound, "log", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::exp>>(csound, "exp", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::exp>>(csound, "exp", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::sqrt>>(csound, "sqrt", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::sqrt>>(csound, "sqrt", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::cos>>(csound, "cos", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::cos>>(csound, "cos", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::sin>>(csound, "sin", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::sin>>(csound, "sin", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::tan>>(csound, "tan", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::tan>>(csound, "tan", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::acos>>(csound, "cosinv", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::acos>>(csound, "cosinv", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::asin>>(csound, "sininv", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::asin>>(csound, "sininv", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::atan>>(csound, "taninv", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::atan>>(csound, "taninv", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::cosh>>(csound, "cosh", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::cosh>>(csound, "cosh", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::sinh>>(csound, "sinh", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::sinh>>(csound, "sinh", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::tanh>>(csound, "tanh", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::tanh>>(csound, "tanh", "k[]", "k[]", csnd::thread::ik); csnd::plugin<ArrayOp<std::cbrt>>(csound, "cbrt", "i[]", "i[]", csnd::thread::i); csnd::plugin<ArrayOp<std::cbrt>>(csound, "cbrt", "k[]", "k[]", csnd::thread::ik); }
7.3. Standard Algorithms
struct PVTrace : csnd::FPlugin<1, 2> { csnd::AuxMem<float> amps; static constexpr char const ∗otypes = "f"; static constexpr char const ∗itypes = "fk"; int init() { if (inargs.fsig_data(0).isSliding()) return csound->init_error(Str("sliding not supported")); 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; } };
7.4. Discussion
8. Conclusions
Conflicts of Interest
References
- Lazzarini, V. The Development of Computer Music Programming Systems. J. New Music Res. 2013, 1, 97–110. [Google Scholar] [CrossRef]
- Dodge, C.; Jerse, T.A. Computer Music: Synthesis, Composition and Performance, 2nd ed.; Schirmer: New York, NY, USA, 1997. [Google Scholar]
- Dannenberg, R.B.; Thompson, N. Real-Time Software Synthesis on Superscalar Architectures. Comput. Music J. 1997, 21, 83–94. [Google Scholar] [CrossRef]
- Lazzarini, V.; ffitch, J.; Yi, S.; Heintz, J.; Brandtsegg, Ø.; McCurdy, I. Csound: A Sound and Music Computing System; Springer: Berlin, Germany, 2016. [Google Scholar]
- Boulanger, R. (Ed.) The Csound Book; MIT Press: Cambridge, MA, USA, 2000. [Google Scholar]
- Sondergaard, H.; Sestoft, P. Referential Transparency, Definiteness and Unfoldability. Acta Inf. 1990, 27, 505–517. [Google Scholar]
- Ffitch, J. Understanding an Opcode in Csound. In The Audio Programming Book; Boulanger, R., Lazzarini, V., Eds.; MIT Press: Cambridge, MA, USA, 2010; pp. 581–615. [Google Scholar]
- Lieberman, H. Machine Tongues IX: Object Oriented Programming. In The Well-tempered Object: Musical Applications of Object-Oriented Software Technology; Pope, S., Ed.; MIT Press: Cambridge, MA, USA, 1991; pp. 18–31. [Google Scholar]
- Stroustrup, B. The C++ Programming Language, 4th ed.; Addison-Wesley: Boston, MA, USA, 2013. [Google Scholar]
- Puckette, M. The Theory and Technique of Computer Music; World Scientific Publishing: New York, NY, USA, 2007. [Google Scholar]
- McCartney, J. Rethinking the Computer Music Language: Supercollider. Comput. Music J. 2002, 26, 61–68. [Google Scholar] [CrossRef]
- Stowell, D. Writing Unit Generator Plug-ins. In The SuperCollider Book; Wilson, S., Cottle, D., Collins, N., Eds.; MIT Press: Cambridge, MA, USA, 2010; pp. 692–720. [Google Scholar]
- Boulanger, R.; Lazzarini, V. (Eds.) The Audio Programming Book; MIT Press: Cambridge, MA, USA, 2010. [Google Scholar]
- Cook, P.; Scavone, G. The Synthesis Toolkit (STK). In Proceedings of the 1999 International Computer Music Conference, Beijing, China, 22–27 October 1999; Volume III, pp. 164–166. [Google Scholar]
- Lazzarini, V. The SndObj Sound Object Library. Organ. Sound 2000, 5, 35–49. [Google Scholar] [CrossRef]
- Lazzarini, V. The Design of a Lightweight DSP Programming Library. In Proceedings of the 14th Sound and Music Computing Conference 2017, Aalto University, Espoo, Finland, 5–8 July 2017; pp. 5–12. [Google Scholar]
- ISO/IEC. ISO International Standard ISO/IEC 14882:2014, Programming Language C++. 2014. Available online: https://www.iso.org/standard/64029.html (accessed on 25 July 2017).
- Lazzarini, V. The Csound Plugin Opcode Framework. In Proceedings of the 14th Sound and Music Computing Conference 2017, Aalto University, Espoo, Finland, 5–8 July 2017; pp. 267–274. [Google Scholar]
- Pope, S. Machine Tongues XI: Object-Oriented Software Design. In The Well-tempered Object: Musical Applications of Object-oriented Software Technology; Pope, S., Ed.; MIT Press: Cambridge, MA, USA, 1991; pp. 32–47. [Google Scholar]
- Cardelli, L.; Wegner, P. On Understanding Types, Data Abstraction, and Polymorphism. ACM Comput. Surv. 1985, 17, 471–523. [Google Scholar] [CrossRef]
- Lippman, S.B. Inside the C++ Object Model; Addison Wesley Longman Publishing Co., Inc.: Redwood City, CA, USA, 1996. [Google Scholar]
- Standard C++ Foundation. How to mix C and C++. Available online: https://isocpp.org/wiki/faq/mixing-c-and-cpp (accessed on 25 July 2017).
- Abrahams, D.; Gurtovoy, A. C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond (C++ in Depth Series); Addison-Wesley Professional: Boston, MA, USA, 2004. [Google Scholar]
- Lazzarini, V. Time-Domain Signal Processing. In The Audio Programming Book; Boulanger, R., Lazzarini, V., Eds.; MIT Press: Cambridge, MA, USA, 2010; pp. 463–512. [Google Scholar]
- Moore, F.R. Elements of Computer Music; Prentice-Hall, Inc.: Upper Saddle River, NJ, USA, 1990. [Google Scholar]
- Lazzarini, V. Spectral Opcodes. In The Audio Programming Book; Boulanger, R., Lazzarini, V., Eds.; MIT Press: Cambridge, MA, USA, 2010; pp. 617–626. [Google Scholar]
- Dolson, M. The Phase Vocoder: A Tutorial. Comput. Music J. 1986, 10, 14–27. [Google Scholar] [CrossRef]
- IEEE/Open Group. The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008. 2016. Available online: http://pubs.opengroup.org/onlinepubs/9699919799/ (accessed on 25 July 2017).
- Wishart, T. Audible Design; Orpheus The Pantomine Ltd.: North Yorkshire, UK, 1996. [Google Scholar]
- ISO/IEC. ISO International Standard ISO/IEC 14882:2011, Programming Language C++. 2011. Available online: https://www.iso.org/standard/50372.html (accessed on 25 July 2017).
ksmps | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 |
---|---|---|---|---|---|---|---|---|
CPU time | 0.996369 | 0.995616 | 0.982088 | 0.975774 | 0.966242 | 1.000936 | 0.960440 | 0.975930 |
© 2017 by the author. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license (http://creativecommons.org/licenses/by/4.0/).
Share and Cite
Lazzarini, V. Supporting an Object-Oriented Approach to Unit Generator Development: The Csound Plugin Opcode Framework. Appl. Sci. 2017, 7, 970. https://doi.org/10.3390/app7100970
Lazzarini V. Supporting an Object-Oriented Approach to Unit Generator Development: The Csound Plugin Opcode Framework. Applied Sciences. 2017; 7(10):970. https://doi.org/10.3390/app7100970
Chicago/Turabian StyleLazzarini, Victor. 2017. "Supporting an Object-Oriented Approach to Unit Generator Development: The Csound Plugin Opcode Framework" Applied Sciences 7, no. 10: 970. https://doi.org/10.3390/app7100970
APA StyleLazzarini, V. (2017). Supporting an Object-Oriented Approach to Unit Generator Development: The Csound Plugin Opcode Framework. Applied Sciences, 7(10), 970. https://doi.org/10.3390/app7100970