/************************** BEGIN dsp.h ******************************** FAUST Architecture File Copyright (C) 2003-2022 GRAME, Centre National de Creation Musicale --------------------------------------------------------------------- This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. EXCEPTION : As a special exception, you may create a larger work that contains this FAUST architecture section and distribute that work under terms of your choice, so long as this FAUST architecture section is not modified. ************************************************************************/ #ifndef __dsp__ #define __dsp__ #include #include #include #include "faust/export.h" #ifndef FAUSTFLOAT #define FAUSTFLOAT float #endif struct FAUST_API UI; struct FAUST_API Meta; /** * DSP memory manager. */ struct FAUST_API dsp_memory_manager { enum MemType { kInt32, kInt32_ptr, kFloat, kFloat_ptr, kDouble, kDouble_ptr, kQuad, kQuad_ptr, kFixedPoint, kFixedPoint_ptr, kObj, kObj_ptr, kSound, kSound_ptr }; virtual ~dsp_memory_manager() = default; /** * Inform the Memory Manager with the number of expected memory zones. * @param count - the number of expected memory zones */ virtual void begin(size_t count) {} /** * Give the Memory Manager information on a given memory zone. * @param name - the memory zone name * @param type - the memory zone type (in MemType) * @param size - the size in unit of the memory type of the memory zone * @param size_bytes - the size in bytes of the memory zone * @param reads - the number of Read access to the zone used to compute one frame * @param writes - the number of Write access to the zone used to compute one frame */ virtual void info(const char* name, MemType type, size_t size, size_t size_bytes, size_t reads, size_t writes) {} /** * Inform the Memory Manager that all memory zones have been described, * to possibly start a 'compute the best allocation strategy' step. */ virtual void end() {} /** * Allocate a memory zone. * @param size - the memory zone size in bytes */ virtual void* allocate(size_t size) = 0; /** * Destroy a memory zone. * @param ptr - the memory zone pointer to be deallocated */ virtual void destroy(void* ptr) = 0; }; /** * Signal processor definition. */ class FAUST_API dsp { public: dsp() = default; virtual ~dsp() = default; /* Return instance number of audio inputs */ virtual int getNumInputs() = 0; /* Return instance number of audio outputs */ virtual int getNumOutputs() = 0; /** * Trigger the ui_interface parameter with instance specific calls * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. * * @param ui_interface - the user interface builder */ virtual void buildUserInterface(UI* ui_interface) = 0; /* Return the sample rate currently used by the instance */ virtual int getSampleRate() = 0; /** * Global init, calls the following methods: * - static class 'classInit': static tables initialization * - 'instanceInit': constants and instance state initialization * * @param sample_rate - the sampling rate in Hz */ virtual void init(int sample_rate) = 0; /** * Init instance state. * * @param sample_rate - the sampling rate in Hz */ virtual void instanceInit(int sample_rate) = 0; /** * Init instance constant state. * * @param sample_rate - the sampling rate in Hz */ virtual void instanceConstants(int sample_rate) = 0; /* Init default control parameters values */ virtual void instanceResetUserInterface() = 0; /* Init instance state (like delay lines...) but keep the control parameter values */ virtual void instanceClear() = 0; /** * Return a clone of the instance. * * @return a copy of the instance on success, otherwise a null pointer. */ virtual ::dsp* clone() = 0; /** * Trigger the Meta* m parameter with instance specific calls to 'declare' (key, value) metadata. * * @param m - the Meta* meta user */ virtual void metadata(Meta* m) = 0; /** * Read all controllers (buttons, sliders, etc.), and update the DSP state to be used by 'frame' or 'compute'. * This method will be filled with the -ec (--external-control) option. */ virtual void control() {} /** * DSP instance computation to process one single frame. * * Note that by default inputs and outputs buffers are supposed to be distinct memory zones, * so one cannot safely write frame(inputs, inputs). * The -inpl option can be used for that, but only in scalar mode for now. * This method will be filled with the -os (--one-sample) option. * * @param inputs - the input audio buffers as an array of FAUSTFLOAT samples (eiher float, double or quad) * @param outputs - the output audio buffers as an array of FAUSTFLOAT samples (eiher float, double or quad) */ virtual void frame(FAUSTFLOAT* inputs, FAUSTFLOAT* outputs) {} /** * DSP instance computation to be called with successive in/out audio buffers. * * Note that by default inputs and outputs buffers are supposed to be distinct memory zones, * so one cannot safely write compute(count, inputs, inputs). * The -inpl compilation option can be used for that, but only in scalar mode for now. * * @param count - the number of frames to compute * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT buffers * (containing either float, double or quad samples) * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT buffers * (containing either float, double or quad samples) */ virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; /** * Alternative DSP instance computation method for use by subclasses, incorporating an additional `date_usec` parameter, * which specifies the timestamp of the first sample in the audio buffers. * * @param date_usec - the timestamp in microsec given by audio driver. By convention timestamp of -1 means 'no timestamp conversion', * events already have a timestamp expressed in frames. * @param count - the number of frames to compute * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) */ virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } }; /** * Generic DSP decorator. */ class FAUST_API decorator_dsp : public ::dsp { protected: ::dsp* fDSP; public: decorator_dsp(::dsp* dsp = nullptr):fDSP(dsp) {} virtual ~decorator_dsp() { delete fDSP; } virtual int getNumInputs() override { return fDSP->getNumInputs(); } virtual int getNumOutputs() override { return fDSP->getNumOutputs(); } virtual void buildUserInterface(UI* ui_interface) override { fDSP->buildUserInterface(ui_interface); } virtual int getSampleRate() override { return fDSP->getSampleRate(); } virtual void init(int sample_rate) override { fDSP->init(sample_rate); } virtual void instanceInit(int sample_rate) override { fDSP->instanceInit(sample_rate); } virtual void instanceConstants(int sample_rate) override { fDSP->instanceConstants(sample_rate); } virtual void instanceResetUserInterface() override { fDSP->instanceResetUserInterface(); } virtual void instanceClear() override { fDSP->instanceClear(); } virtual decorator_dsp* clone() override { return new decorator_dsp(fDSP->clone()); } virtual void metadata(Meta* m) override { fDSP->metadata(m); } // Beware: subclasses usually have to overload the two 'compute' methods virtual void control() override { fDSP->control(); } virtual void frame(FAUSTFLOAT* inputs, FAUSTFLOAT* outputs) override { fDSP->frame(inputs, outputs); } virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) override { fDSP->compute(count, inputs, outputs); } virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) override { fDSP->compute(date_usec, count, inputs, outputs); } }; /** * DSP factory class, used with LLVM and Interpreter backends * to create DSP instances from a compiled DSP program. */ class FAUST_API dsp_factory { protected: // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); virtual ~dsp_factory() = default; public: /* Return factory name */ virtual std::string getName() = 0; /* Return factory SHA key */ virtual std::string getSHAKey() = 0; /* Return factory expanded DSP code */ virtual std::string getDSPCode() = 0; /* Return factory compile options */ virtual std::string getCompileOptions() = 0; /* Get the Faust DSP factory list of library dependancies */ virtual std::vector getLibraryList() = 0; /* Get the list of all used includes */ virtual std::vector getIncludePathnames() = 0; /* Get warning messages list for a given compilation */ virtual std::vector getWarningMessages() = 0; /* Return JSON description of the DSP (UI + metadata) */ virtual std::string getJSON() = 0; /* Create a new DSP instance, to be deleted with C++ 'delete' */ virtual ::dsp* createDSPInstance() = 0; /* Static tables initialization, possibly implemened in sub-classes*/ virtual void classInit(int sample_rate) {}; /* Set a custom memory manager to be used when creating instances */ virtual void setMemoryManager(dsp_memory_manager* manager) = 0; /* Return the currently set custom memory manager */ virtual dsp_memory_manager* getMemoryManager() = 0; }; // Denormal handling #if defined (__SSE__) #include #endif class FAUST_API ScopedNoDenormals { private: intptr_t fpsr = 0; void setFpStatusRegister(intptr_t fpsr_aux) noexcept { #if defined (__arm64__) || defined (__aarch64__) asm volatile("msr fpcr, %0" : : "ri" (fpsr_aux)); #elif defined (__SSE__) // The volatile keyword here is needed to workaround a bug in AppleClang 13.0 // which aggressively optimises away the variable otherwise volatile uint32_t fpsr_w = static_cast(fpsr_aux); _mm_setcsr(fpsr_w); #endif } void getFpStatusRegister() noexcept { #if defined (__arm64__) || defined (__aarch64__) asm volatile("mrs %0, fpcr" : "=r" (fpsr)); #elif defined (__SSE__) fpsr = static_cast(_mm_getcsr()); #endif } public: ScopedNoDenormals() noexcept { #if defined (__arm64__) || defined (__aarch64__) intptr_t mask = (1 << 24 /* FZ */); #elif defined (__SSE__) #if defined (__SSE2__) intptr_t mask = 0x8040; #else intptr_t mask = 0x8000; #endif #else intptr_t mask = 0x0000; #endif getFpStatusRegister(); setFpStatusRegister(fpsr | mask); } ~ScopedNoDenormals() noexcept { setFpStatusRegister(fpsr); } }; #define AVOIDDENORMALS ScopedNoDenormals ftz_scope; #endif /************************** END dsp.h **************************/