From 31f84f0c0bbc0c772740b924487c7663c8a6134f Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Sat, 16 Dec 2023 21:49:38 -0800 Subject: [PATCH 1/7] Formatting --- dsp/ImpulseResponse.cpp | 3 ++- dsp/NoiseGate.cpp | 2 +- tools/benchmodel.cpp | 2 -- tools/loadmodel.cpp | 50 +++++++++++++++++++++++------------------ 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/dsp/ImpulseResponse.cpp b/dsp/ImpulseResponse.cpp index e737fbb..6850f53 100644 --- a/dsp/ImpulseResponse.cpp +++ b/dsp/ImpulseResponse.cpp @@ -11,7 +11,8 @@ #include "ImpulseResponse.h" dsp::ImpulseResponse::ImpulseResponse(const char* fileName, const double sampleRate) -: mWavState(dsp::wav::LoadReturnCode::ERROR_OTHER), mSampleRate(sampleRate) +: mWavState(dsp::wav::LoadReturnCode::ERROR_OTHER) +, mSampleRate(sampleRate) { // Try to load the WAV this->mWavState = dsp::wav::Load(fileName, this->mRawAudio, this->mRawAudioSampleRate); diff --git a/dsp/NoiseGate.cpp b/dsp/NoiseGate.cpp index 212a425..0ce382b 100644 --- a/dsp/NoiseGate.cpp +++ b/dsp/NoiseGate.cpp @@ -5,7 +5,7 @@ // Created by Steven Atkinson on 2/5/23. // -#include // std::clamp +#include // std::clamp #include // memcpy #include // pow #include diff --git a/tools/benchmodel.cpp b/tools/benchmodel.cpp index 5b2dd8a..ec07ad7 100644 --- a/tools/benchmodel.cpp +++ b/tools/benchmodel.cpp @@ -7,8 +7,6 @@ // Test to benchmark model loading and running - - #include #include #include diff --git a/tools/loadmodel.cpp b/tools/loadmodel.cpp index 39c2ef1..42df81e 100644 --- a/tools/loadmodel.cpp +++ b/tools/loadmodel.cpp @@ -10,28 +10,30 @@ #include #include "dsp/dsp.h" -class Dummy: public dsp::DSP { - public: - ~Dummy() { - _DeallocateOutputPointers(); - } - DSP_SAMPLE** Process(DSP_SAMPLE** inputs, const size_t numChannels, const size_t numFrames) override { - if (numChannels > _GetNumChannels()) { +class Dummy : public dsp::DSP +{ +public: + ~Dummy() { _DeallocateOutputPointers(); } + DSP_SAMPLE** Process(DSP_SAMPLE** inputs, const size_t numChannels, const size_t numFrames) override + { + if (numChannels > _GetNumChannels()) + { throw std::runtime_error("Asked to process too many channels!\n"); } - if (numFrames > _GetNumFrames()) { + if (numFrames > _GetNumFrames()) + { throw std::runtime_error("Asked to process too many samples!\n"); } - for (int c = 0; c < numChannels; c++) { - for (int f=0; f Date: Sat, 16 Dec 2023 21:50:11 -0800 Subject: [PATCH 2/7] Start pulling in code --- Dependencies/WDL/heapbuf.h | 383 ++++++++++++++++++++++++++++++++++++ Dependencies/WDL/ptrlist.h | 273 +++++++++++++++++++++++++ Dependencies/WDL/wdltypes.h | 329 +++++++++++++++++++++++++++++++ dsp/ResamplingContainer.h | 357 +++++++++++++++++++++++++++++++++ dsp/_LanczosResampler.h | 369 ++++++++++++++++++++++++++++++++++ 5 files changed, 1711 insertions(+) create mode 100644 Dependencies/WDL/heapbuf.h create mode 100644 Dependencies/WDL/ptrlist.h create mode 100644 Dependencies/WDL/wdltypes.h create mode 100644 dsp/ResamplingContainer.h create mode 100644 dsp/_LanczosResampler.h diff --git a/Dependencies/WDL/heapbuf.h b/Dependencies/WDL/heapbuf.h new file mode 100644 index 0000000..bf2a632 --- /dev/null +++ b/Dependencies/WDL/heapbuf.h @@ -0,0 +1,383 @@ +/* + WDL - heapbuf.h + Copyright (C) 2005 and later Cockos Incorporated + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +*/ + +/* + + This file provides the interface and implementation for WDL_HeapBuf, a simple + malloc() wrapper for resizeable blocks. + + Also in this file is WDL_TypedBuf which is a templated version WDL_HeapBuf + that manages type and type-size. + +*/ + +#ifndef _WDL_HEAPBUF_H_ +#define _WDL_HEAPBUF_H_ + +#ifndef WDL_HEAPBUF_IMPL_ONLY + +#ifdef WDL_HEAPBUF_TRACE +#include +#define WDL_HEAPBUF_TRACEPARM(x) ,(x) +#else +#define WDL_HEAPBUF_TRACEPARM(x) +#endif + +#include "wdltypes.h" + +class WDL_HeapBuf +{ + public: + // interface +#ifdef WDL_HEAPBUF_INTF_ONLY + void *Resize(int newsize, bool resizedown=true); + void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false); +#endif + void *Get() const { return m_size?m_buf:NULL; } // returns NULL if size is 0 + void *GetFast() const { return m_buf; } // returns last buffer if size is 0 + int GetSize() const { return m_size; } + void *GetAligned(int align) const { return (void *)(((UINT_PTR)Get() + (align-1)) & ~(UINT_PTR)(align-1)); } + + void SetGranul(int granul) { m_granul = granul; } + int GetGranul() const { return m_granul; } + + void *ResizeOK(int newsize, bool resizedown = true) { void *p=Resize(newsize, resizedown); return GetSize() == newsize ? p : NULL; } + + WDL_HeapBuf(const WDL_HeapBuf &cp) + { + m_buf=0; + CopyFrom(&cp,true); + } + WDL_HeapBuf &operator=(const WDL_HeapBuf &cp) + { + CopyFrom(&cp,false); + return *this; + } + + + + #ifndef WDL_HEAPBUF_TRACE + explicit WDL_HeapBuf(int granul=4096) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) + { + } + ~WDL_HeapBuf() + { + free(m_buf); + } + #else + explicit WDL_HeapBuf(int granul=4096, const char *tracetype="WDL_HeapBuf" + ) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) + { + m_tracetype = tracetype; + wdl_log("WDL_HeapBuf: created type: %s granul=%d\n",tracetype,granul); + } + ~WDL_HeapBuf() + { + wdl_log("WDL_HeapBuf: destroying type: %s (alloc=%d, size=%d)\n",m_tracetype,m_alloc,m_size); + free(m_buf); + } + #endif + +#endif // !WDL_HEAPBUF_IMPL_ONLY + + // implementation bits +#ifndef WDL_HEAPBUF_INTF_ONLY + #ifdef WDL_HEAPBUF_IMPL_ONLY + void *WDL_HeapBuf::Resize(int newsize, bool resizedown) + #else + void *Resize(int newsize, bool resizedown=true) + #endif + { + if (newsize<0) newsize=0; + #ifdef DEBUG_TIGHT_ALLOC // horribly slow, do not use for release builds + if (newsize == m_size) return m_buf; + + int a = newsize; + if (a > m_size) a=m_size; + void *newbuf = newsize ? malloc(newsize) : 0; + if (!newbuf && newsize) + { + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + WDL_HEAPBUF_ONMALLOCFAIL(newsize) + #endif + return m_buf; + } + if (newbuf&&m_buf) memcpy(newbuf,m_buf,a); + m_size=m_alloc=newsize; + free(m_buf); + return m_buf=newbuf; + #endif + + if (newsize!=m_size || (resizedown && newsize < m_alloc/2)) + { + int resizedown_under = 0; + if (resizedown && newsize < m_size) + { + // shrinking buffer: only shrink if allocation decreases to min(alloc/2, alloc-granul*4) or 0 + resizedown_under = m_alloc - (m_granul<<2); + if (resizedown_under > m_alloc/2) resizedown_under = m_alloc/2; + if (resizedown_under < 1) resizedown_under=1; + } + + if (newsize > m_alloc || newsize < resizedown_under) + { + int granul=newsize/2; + int newalloc; + if (granul < m_granul) granul=m_granul; + + if (newsize<1) newalloc=0; + else if (m_granul<4096) newalloc=newsize+granul; + else + { + granul &= ~4095; + if (granul< 4096) granul=4096; + else if (granul>4*1024*1024) granul=4*1024*1024; + newalloc = ((newsize + granul + 96)&~4095)-96; + } + + if (newalloc != m_alloc) + { + + #ifdef WDL_HEAPBUF_TRACE + wdl_log("WDL_HeapBuf: type %s realloc(%d) from %d\n",m_tracetype,newalloc,m_alloc); + #endif + if (newalloc <= 0) + { + free(m_buf); + m_buf=0; + m_alloc=0; + m_size=0; + return 0; + } + void *nbuf=realloc(m_buf,newalloc); + if (!nbuf) + { + if (!(nbuf=malloc(newalloc))) + { + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + WDL_HEAPBUF_ONMALLOCFAIL(newalloc); + #endif + return m_size?m_buf:0; // failed, do not resize + } + + if (m_buf) + { + int sz=newsize0) memcpy(nbuf,m_buf,sz); + free(m_buf); + } + } + + m_buf=nbuf; + m_alloc=newalloc; + } // alloc size change + } // need size up or down + m_size=newsize; + } // size change + return m_size?m_buf:0; + } + + #ifdef WDL_HEAPBUF_IMPL_ONLY + void WDL_HeapBuf::CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig) + #else + void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false) + #endif + { + if (exactCopyOfConfig) // copy all settings + { + free(m_buf); + + #ifdef WDL_HEAPBUF_TRACE + m_tracetype = hb->m_tracetype; + #endif + m_granul = hb->m_granul; + + m_size=m_alloc=0; + m_buf=hb->m_buf && hb->m_alloc>0 ? malloc(m_alloc = hb->m_alloc) : NULL; + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + if (!m_buf && m_alloc) { WDL_HEAPBUF_ONMALLOCFAIL(m_alloc) } ; + #endif + if (m_buf) memcpy(m_buf,hb->m_buf,m_size = hb->m_size); + else m_alloc=0; + } + else // copy just the data + size + { + const int newsz=hb->GetSize(); + Resize(newsz,true); + if (GetSize()!=newsz) Resize(0); + else memcpy(Get(),hb->Get(),newsz); + } + } + +#endif // ! WDL_HEAPBUF_INTF_ONLY + +#ifndef WDL_HEAPBUF_IMPL_ONLY + + private: + void *m_buf; + int m_alloc; + int m_size; + int m_granul; + + #if defined(_WIN64) || defined(__LP64__) + public: + int ___pad; // keep size 8 byte aligned + #endif + + #ifdef WDL_HEAPBUF_TRACE + const char *m_tracetype; + #endif + +}; + +template class WDL_TypedBuf +{ + public: + PTRTYPE *Get() const { return (PTRTYPE *) m_hb.Get(); } + PTRTYPE *GetFast() const { return (PTRTYPE *) m_hb.GetFast(); } + int GetSize() const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE); } + int GetSizeBytes() const { return m_hb.GetSize(); } + + PTRTYPE *Resize(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.Resize(newsize*sizeof(PTRTYPE),resizedown); } + PTRTYPE *ResizeOK(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.ResizeOK(newsize*sizeof(PTRTYPE), resizedown); } + + void SetToZero() { memset(m_hb.Get(), 0, m_hb.GetSize()); } + + PTRTYPE *GetAligned(int align) const { return (PTRTYPE *) m_hb.GetAligned(align); } + + PTRTYPE *Add(PTRTYPE val) + { + const int sz=GetSize(); + PTRTYPE* p=ResizeOK(sz+1,false); + if (p) + { + p[sz]=val; + return p+sz; + } + return NULL; + } + PTRTYPE *Add(const PTRTYPE *buf, int bufsz) + { + if (bufsz>0) + { + const int sz=GetSize(); + PTRTYPE* p=ResizeOK(sz+bufsz,false); + if (p) + { + p+=sz; + if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); + else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); + return p; + } + } + return NULL; + } + PTRTYPE *Set(const PTRTYPE *buf, int bufsz) + { + if (bufsz>=0) + { + PTRTYPE* p=ResizeOK(bufsz,false); + if (p) + { + if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); + else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); + return p; + } + } + return NULL; + } + PTRTYPE* Insert(PTRTYPE val, int idx) + { + const int sz=GetSize(); + if (idx >= 0 && idx <= sz) + { + PTRTYPE* p=ResizeOK(sz+1,false); + if (p) + { + memmove(p+idx+1, p+idx, (sz-idx)*sizeof(PTRTYPE)); + p[idx]=val; + return p+idx; + } + } + return NULL; + } + + void Delete(int idx) + { + PTRTYPE* p=Get(); + const int sz=GetSize(); + if (idx >= 0 && idx < sz) + { + memmove(p+idx, p+idx+1, (sz-idx-1)*sizeof(PTRTYPE)); + Resize(sz-1,false); + } + } + + void SetGranul(int gran) { m_hb.SetGranul(gran); } + + int Find(PTRTYPE val) const + { + const PTRTYPE* p=Get(); + const int sz=GetSize(); + int i; + for (i=0; i < sz; ++i) if (p[i] == val) return i; + return -1; + } + +#ifndef WDL_HEAPBUF_TRACE + explicit WDL_TypedBuf(int granul=4096) : m_hb(granul) { } +#else + explicit WDL_TypedBuf(int granul=4096, const char *tracetype="WDL_TypedBuf") : m_hb(granul WDL_HEAPBUF_TRACEPARM(tracetype)) { } +#endif + ~WDL_TypedBuf() + { + } + + WDL_HeapBuf *GetHeapBuf() { return &m_hb; } + const WDL_HeapBuf *GetHeapBuf() const { return &m_hb; } + + int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to delete item. returns number deleted + { + const int sz = GetSize(); + int cnt=0; + PTRTYPE *rd = Get(), *wr = rd; + for (int x = 0; x < sz; x ++) + { + if (!proc(rd,ctx)) + { + if (rd != wr) *wr=*rd; + wr++; + cnt++; + } + rd++; + } + if (cnt < sz) Resize(cnt,false); + return sz - cnt; + } + + private: + WDL_HeapBuf m_hb; +}; + +#endif // ! WDL_HEAPBUF_IMPL_ONLY + +#endif // _WDL_HEAPBUF_H_ diff --git a/Dependencies/WDL/ptrlist.h b/Dependencies/WDL/ptrlist.h new file mode 100644 index 0000000..eec7a98 --- /dev/null +++ b/Dependencies/WDL/ptrlist.h @@ -0,0 +1,273 @@ +/* + WDL - ptrlist.h + Copyright (C) 2005 and later, Cockos Incorporated + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +*/ + +/* + + This file provides a simple templated class for a list of pointers. By default this list + doesn't free any of the pointers, but you can call Empty(true) or Delete(x,true) to delete the pointer, + or you can use Empty(true,free) etc to call free (or any other function). + + Note: on certain compilers, instantiating with WDL_PtrList bla; will give a warning, since + the template will create code for "delete (void *)x;" which isn't technically valid. Oh well. + +*/ + +#ifndef _WDL_PTRLIST_H_ +#define _WDL_PTRLIST_H_ + +#include "heapbuf.h" + +template class WDL_PtrList +{ + public: + explicit WDL_PtrList(int defgran=4096) : m_hb(defgran WDL_HEAPBUF_TRACEPARM("WDL_PtrList")) + { + } + + ~WDL_PtrList() + { + } + + PTRTYPE **GetList() const { return (PTRTYPE**)m_hb.Get(); } + PTRTYPE *Get(INT_PTR index) const + { + PTRTYPE **list = (PTRTYPE**)m_hb.Get(); + if (list && (UINT_PTR)index < (UINT_PTR)(m_hb.GetSize()/sizeof(PTRTYPE *))) return list[index]; + return NULL; + } + + int GetSize(void) const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE *); } + + int Find(const PTRTYPE *p) const + { + if (p) + { + PTRTYPE **list=(PTRTYPE **)m_hb.Get(); + int x; + const int n = GetSize(); + for (x = 0; x < n; x ++) if (list[x] == p) return x; + } + return -1; + } + int FindR(const PTRTYPE *p) const + { + if (p) + { + PTRTYPE **list=(PTRTYPE **)m_hb.Get(); + int x = GetSize(); + while (--x >= 0) if (list[x] == p) return x; + } + return -1; + } + + PTRTYPE *Add(PTRTYPE *item) + { + const int s=GetSize(); + PTRTYPE **list=(PTRTYPE **)m_hb.ResizeOK((s+1)*(unsigned int)sizeof(PTRTYPE*),false); + if (list) + { + list[s]=item; + return item; + } + return NULL; + } + + PTRTYPE *Set(int index, PTRTYPE *item) + { + PTRTYPE **list=(PTRTYPE **)m_hb.Get(); + if (list && index >= 0 && index < GetSize()) return list[index]=item; + return NULL; + } + + PTRTYPE *Insert(int index, PTRTYPE *item) + { + int s=GetSize(); + PTRTYPE **list = (PTRTYPE **)m_hb.ResizeOK((s+1)*(unsigned int)sizeof(PTRTYPE*),false); + + if (!list) return item; + + if (index<0) index=0; + + int x; + for (x = s; x > index; x --) list[x]=list[x-1]; + return (list[x] = item); + } + int FindSorted(const PTRTYPE *p, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) const + { + bool m; + int i = LowerBound(p,&m,compar); + return m ? i : -1; + } + PTRTYPE *InsertSorted(PTRTYPE *item, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) + { + bool m; + return Insert(LowerBound(item,&m,compar),item); + } + + void Delete(int index) + { + PTRTYPE **list=GetList(); + int size=GetSize(); + if (list && index >= 0 && index < size) + { + if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + } + } + void Delete(int index, bool wantDelete, void (*delfunc)(void *)=NULL) + { + PTRTYPE **list=GetList(); + int size=GetSize(); + if (list && index >= 0 && index < size) + { + if (wantDelete) + { + if (delfunc) delfunc(Get(index)); + else delete Get(index); + } + if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + } + } + void Delete(int index, void (*delfunc)(PTRTYPE *)) + { + PTRTYPE **list=GetList(); + int size=GetSize(); + if (list && index >= 0 && index < size) + { + if (delfunc) delfunc(Get(index)); + if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + } + } + void DeletePtr(const PTRTYPE *p) { Delete(Find(p)); } + void DeletePtr(const PTRTYPE *p, bool wantDelete, void (*delfunc)(void *)=NULL) { Delete(Find(p),wantDelete,delfunc); } + void DeletePtr(const PTRTYPE *p, void (*delfunc)(PTRTYPE *)) { Delete(Find(p),delfunc); } + + void Empty() + { + m_hb.Resize(0,false); + } + void Empty(bool wantDelete, void (*delfunc)(void *)=NULL) + { + if (wantDelete) + { + int x; + for (x = GetSize()-1; x >= 0; x --) + { + PTRTYPE* p = Get(x); + if (p) + { + if (delfunc) delfunc(p); + else delete p; + } + m_hb.Resize(x*(unsigned int)sizeof(PTRTYPE *),false); + } + } + m_hb.Resize(0,false); + } + void Empty(void (*delfunc)(PTRTYPE *)) + { + int x; + for (x = GetSize()-1; x >= 0; x --) + { + PTRTYPE* p = Get(x); + if (delfunc && p) delfunc(p); + m_hb.Resize(x*(unsigned int)sizeof(PTRTYPE *),false); + } + } + void EmptySafe(bool wantDelete=false,void (*delfunc)(void *)=NULL) + { + if (!wantDelete) Empty(); + else + { + WDL_PtrList tmp; + int x; + for(x=0;x 0) a = b+1; + else if (cmp < 0) c = b; + else + { + *ismatch = true; + return b; + } + } + *ismatch = false; + return a; + } + + void Compact() { m_hb.Resize(m_hb.GetSize(),true); } + + + int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to remove item. returns number removed + { + const int sz = GetSize(); + int cnt=0; + PTRTYPE **rd = GetList(), **wr = rd; + for (int x = 0; x < sz; x ++) + { + if (!proc(*rd,ctx)) + { + if (rd != wr) *wr=*rd; + wr++; + cnt++; + } + rd++; + } + if (cnt < sz) m_hb.Resize(cnt * sizeof(PTRTYPE*),false); + return sz - cnt; + } + + private: + WDL_HeapBuf m_hb; + +}; + + +template class WDL_PtrList_DeleteOnDestroy : public WDL_PtrList +{ +public: + explicit WDL_PtrList_DeleteOnDestroy(void (*delfunc)(void *)=NULL, int defgran=4096) : WDL_PtrList(defgran), m_delfunc(delfunc) { } + ~WDL_PtrList_DeleteOnDestroy() + { + WDL_PtrList::EmptySafe(true,m_delfunc); + } +private: + void (*m_delfunc)(void *); +}; + +#endif + diff --git a/Dependencies/WDL/wdltypes.h b/Dependencies/WDL/wdltypes.h new file mode 100644 index 0000000..7869e7b --- /dev/null +++ b/Dependencies/WDL/wdltypes.h @@ -0,0 +1,329 @@ +#ifndef _WDLTYPES_ +#define _WDLTYPES_ + +#ifdef _MSC_VER + +typedef __int64 WDL_INT64; +typedef unsigned __int64 WDL_UINT64; + +#else + +typedef long long WDL_INT64; +typedef unsigned long long WDL_UINT64; + +#endif + +#ifdef _MSC_VER + #define WDL_UINT64_CONST(x) (x##ui64) + #define WDL_INT64_CONST(x) (x##i64) +#else + #define WDL_UINT64_CONST(x) (x##ULL) + #define WDL_INT64_CONST(x) (x##LL) +#endif + +#ifdef _WIN32 + #define WDL_PRI_UINT64 "I64u" + #define WDL_PRI_INT64 "I64d" +#else + #define WDL_PRI_UINT64 "llu" + #define WDL_PRI_INT64 "lld" +#endif + +#if !defined(_MSC_VER) || _MSC_VER > 1200 +#define WDL_DLGRET INT_PTR CALLBACK +#else +#define WDL_DLGRET BOOL CALLBACK +#endif + + +#ifdef _WIN32 +#include +#include +#else +#include +typedef intptr_t INT_PTR; +typedef uintptr_t UINT_PTR; +#endif +#include + +#if defined(__ppc__) || !defined(__cplusplus) +typedef char WDL_bool; +#else +typedef bool WDL_bool; +#endif + +#ifndef GWLP_USERDATA +#define GWLP_USERDATA GWL_USERDATA +#define GWLP_WNDPROC GWL_WNDPROC +#define GWLP_HINSTANCE GWL_HINSTANCE +#define GWLP_HWNDPARENT GWL_HWNDPARENT +#define DWLP_USER DWL_USER +#define DWLP_DLGPROC DWL_DLGPROC +#define DWLP_MSGRESULT DWL_MSGRESULT +#define SetWindowLongPtr(a,b,c) SetWindowLong(a,b,c) +#define GetWindowLongPtr(a,b) GetWindowLong(a,b) +#define SetWindowLongPtrW(a,b,c) SetWindowLongW(a,b,c) +#define GetWindowLongPtrW(a,b) GetWindowLongW(a,b) +#define SetWindowLongPtrA(a,b,c) SetWindowLongA(a,b,c) +#define GetWindowLongPtrA(a,b) GetWindowLongA(a,b) + +#define GCLP_WNDPROC GCL_WNDPROC +#define GCLP_HICON GCL_HICON +#define GCLP_HICONSM GCL_HICONSM +#define SetClassLongPtr(a,b,c) SetClassLong(a,b,c) +#define GetClassLongPtr(a,b) GetClassLong(a,b) +#endif + +#if !defined(WDL_BIG_ENDIAN) && !defined(WDL_LITTLE_ENDIAN) + #ifdef __ppc__ + #define WDL_BIG_ENDIAN + #else + #define WDL_LITTLE_ENDIAN + #endif +#endif + +#if defined(WDL_BIG_ENDIAN) && defined(WDL_LITTLE_ENDIAN) +#error WDL_BIG_ENDIAN and WDL_LITTLE_ENDIAN both defined +#endif + + +#ifdef __GNUC__ +// for structures that contain doubles, or doubles in structures that are after stuff of questionable alignment (for OSX/linux) + #define WDL_FIXALIGN __attribute__ ((aligned (8))) +// usage: void func(int a, const char *fmt, ...) WDL_VARARG_WARN(printf,2,3); // note: if member function, this pointer is counted as well, so as member function that would be 3,4 + #define WDL_VARARG_WARN(x,n,s) __attribute__ ((format (x,n,s))) + #define WDL_STATICFUNC_UNUSED __attribute__((unused)) + +#else + #define WDL_FIXALIGN + #define WDL_VARARG_WARN(x,n,s) + #define WDL_STATICFUNC_UNUSED +#endif + +#ifndef WDL_WANT_NEW_EXCEPTIONS +#if defined(__cplusplus) +#include +#define WDL_NEW (std::nothrow) +#endif +#else +#define WDL_NEW +#endif + + +#if !defined(max) && defined(WDL_DEFINE_MINMAX) +#define max(x,y) ((x)<(y)?(y):(x)) +#define min(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef wdl_max +#define wdl_max(x,y) ((x)<(y)?(y):(x)) +#define wdl_min(x,y) ((x)<(y)?(x):(y)) +#define wdl_abs(x) ((x)<0 ? -(x) : (x)) +#define wdl_clamp(x,minv,maxv) (WDL_NOT_NORMALLY((maxv) < (minv)) || (x) < (minv) ? (minv) : ((x) > (maxv) ? (maxv) : (x))) +#endif + +#ifndef _WIN32 + #ifndef strnicmp + #define strnicmp(x,y,z) strncasecmp(x,y,z) + #endif + #ifndef stricmp + #define stricmp(x,y) strcasecmp(x,y) + #endif +#endif + +#ifdef WDL_BACKSLASHES_ARE_ORDINARY +#define WDL_IS_DIRCHAR(x) ((x) == '/') +#else +// for multi-platform applications it seems better to treat backslashes as directory separators even if it +// isn't supported by the underying system (for resolving filenames, etc) + #ifdef _WIN32 + #define WDL_IS_DIRCHAR(x) ((x) == '\\' || (x) == '/') + #else + #define WDL_IS_DIRCHAR(x) ((x) == '/' || (x) == '\\') + #endif +#endif + +#if defined(_WIN32) && !defined(WDL_BACKSLASHES_ARE_ORDINARY) +#define WDL_DIRCHAR '\\' +#define WDL_DIRCHAR_STR "\\" +#else +#define WDL_DIRCHAR '/' +#define WDL_DIRCHAR_STR "/" +#endif + +#if defined(_WIN32) || defined(__APPLE__) + // on __APPLE__ we should ideally check the filesystem for case-sensitivity, assuming a case-insensitive-only match + #define wdl_filename_cmp(x,y) stricmp(x,y) + #define wdl_filename_cmpn(x,y,n) strnicmp(x,y,n) +#else + #define wdl_filename_cmp(x,y) strcmp(x,y) + #define wdl_filename_cmpn(x,y,n) strncmp(x,y,n) +#endif + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) + #define WDL_likely(x) (__builtin_expect(!!(x),1)) + #define WDL_unlikely(x) (__builtin_expect(!!(x),0)) +#else + #define WDL_likely(x) (!!(x)) + #define WDL_unlikely(x) (!!(x)) +#endif + +#if defined(_DEBUG) || defined(DEBUG) +#include + + #ifdef _MSC_VER + // msvc assert failure allows message loop to run, potentially resulting in recursive asserts + static LONG WDL_ASSERT_INTERNALCNT; + static int WDL_ASSERT_END() { WDL_ASSERT_INTERNALCNT=0; return 0; } + static int WDL_ASSERT_BEGIN() { return InterlockedCompareExchange(&WDL_ASSERT_INTERNALCNT,1,0) == 0; } + #define WDL_ASSERT(x) do { if (WDL_ASSERT_BEGIN()) { assert(x); WDL_ASSERT_END(); } } while(0) + #else + #define WDL_ASSERT_BEGIN() (1) + #define WDL_ASSERT_END() (0) + #define WDL_ASSERT(x) assert(x) + #endif + #define WDL_NORMALLY(x) ((x) ? 1 : (WDL_ASSERT_BEGIN() && (assert(0/*ignorethis*/ && (x)),WDL_ASSERT_END()))) + #define WDL_NOT_NORMALLY(x) ((x) ? !WDL_ASSERT_BEGIN() || (assert(0/*ignorethis*/ && !(x)),!WDL_ASSERT_END()) : 0) +#else + #define WDL_ASSERT(x) + #define WDL_NORMALLY(x) WDL_likely(x) + #define WDL_NOT_NORMALLY(x) WDL_unlikely(x) +#endif + + +typedef unsigned int WDL_TICKTYPE; + +static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE(WDL_TICKTYPE current, WDL_TICKTYPE refstart, int len) // current >= refstart && current < refstart+len +{ + WDL_ASSERT(len > 0); + return (current - refstart) < (WDL_TICKTYPE)len; +} + +static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE_ENDING_AT(WDL_TICKTYPE current, WDL_TICKTYPE refend, int len) // current >= refend-len && current < refend +{ + const WDL_TICKTYPE refstart = refend - len; + WDL_ASSERT(len > 0); + return (current - refstart) < (WDL_TICKTYPE)len; + //return ((refend-1) - current) < (WDL_TICKTYPE)len; +} + +// use this if you want validate that nothing that includes wdltypes.h calls fopen() directly on win32 +// #define WDL_CHECK_FOR_NON_UTF8_FOPEN + +#if defined(WDL_CHECK_FOR_NON_UTF8_FOPEN) && !defined(_WDL_WIN32_UTF8_H_) + #ifdef fopen + #undef fopen + #endif + #include + static WDL_STATICFUNC_UNUSED FILE *WDL_fopenA(const char *fn, const char *mode) { return fopen(fn,mode); } + #define fopen this_should_be_fopenUTF8_include_win32_utf8.h +#else + // callers of WDL_fopenA don't mind being non-UTF8-compatible on win32 + // (this could map to either fopen() or fopenUTF8() + #define WDL_fopenA(fn,mode) fopen(fn,mode) +#endif + +#ifndef WDL_ALLOW_UNSIGNED_DEFAULT_CHAR +typedef char wdl_assert_failed_unsigned_char[((char)-1) > 0 ? -1 : 1]; +#endif + +// wdl_log() / printf() wrapper. no-op on release builds +#if !defined(_DEBUG) && !defined(WDL_LOG_ON_RELEASE) + static void WDL_STATICFUNC_UNUSED WDL_VARARG_WARN(printf,1,2) wdl_log(const char *format, ...) { } +#elif defined(_WIN32) + static void WDL_STATICFUNC_UNUSED WDL_VARARG_WARN(printf,1,2) wdl_log(const char *format, ...) + { + int rv; + va_list va; + + char tmp[3800]; + va_start(va,format); + tmp[0]=0; + rv=_vsnprintf(tmp,sizeof(tmp),format,va); // returns -1 if over, and does not null terminate, ugh + va_end(va); + + if (rv < 0 || rv>=(int)sizeof(tmp)-1) tmp[sizeof(tmp)-1]=0; + OutputDebugStringA(tmp); + } +#else + #define wdl_log printf +#endif + +static void WDL_STATICFUNC_UNUSED wdl_bswap_copy(void *bout, const void *bin, size_t nelem, size_t elemsz) +{ + char p[8], po[8]; + WDL_ASSERT(elemsz > 0); + if (elemsz > 1 && WDL_NORMALLY(elemsz <= sizeof(p))) + { + size_t i,x; + for (i = 0; i < nelem; i ++) + { + memcpy(p,bin,elemsz); + for (x = 0; x < elemsz; x ++) po[x]=p[elemsz-1-x]; + memcpy(bout,po,elemsz); + bin = (const char *)bin + elemsz; + bout = (char *)bout + elemsz; + } + } + else if (bout != bin) + memmove(bout,bin,elemsz * nelem); +} + +static void WDL_STATICFUNC_UNUSED wdl_memcpy_le(void *bout, const void *bin, size_t nelem, size_t elemsz) +{ + WDL_ASSERT(elemsz > 0 && elemsz <= 8); +#ifdef WDL_BIG_ENDIAN + if (elemsz > 1) wdl_bswap_copy(bout,bin,nelem,elemsz); + else +#endif + if (bout != bin) memmove(bout,bin,elemsz * nelem); +} + +static void WDL_STATICFUNC_UNUSED wdl_memcpy_be(void *bout, const void *bin, size_t nelem, size_t elemsz) +{ + WDL_ASSERT(elemsz > 0 && elemsz <= 8); +#ifdef WDL_LITTLE_ENDIAN + if (elemsz > 1) wdl_bswap_copy(bout,bin,nelem,elemsz); + else +#endif + if (bout != bin) memmove(bout,bin,elemsz * nelem); +} + +static void WDL_STATICFUNC_UNUSED wdl_mem_store_int(void *bout, int v) +{ + memcpy(bout,&v,sizeof(v)); +} + +static void WDL_STATICFUNC_UNUSED wdl_mem_store_int_le(void *bout, int v) +{ + wdl_memcpy_le(bout,&v,1,sizeof(v)); +} + +static void WDL_STATICFUNC_UNUSED wdl_mem_store_int_be(void *bout, int v) +{ + wdl_memcpy_be(bout,&v,1,sizeof(v)); +} + +static int WDL_STATICFUNC_UNUSED wdl_mem_load_int(const void *rd) +{ + int v; + memcpy(&v,rd,sizeof(v)); + return v; +} + +static int WDL_STATICFUNC_UNUSED wdl_mem_load_int_le(const void *rd) +{ + int v; + wdl_memcpy_le(&v,rd,1,sizeof(v)); + return v; +} + +static int WDL_STATICFUNC_UNUSED wdl_mem_load_int_be(const void *rd) +{ + int v; + wdl_memcpy_be(&v,rd,1,sizeof(v)); + return v; +} + + +#endif diff --git a/dsp/ResamplingContainer.h b/dsp/ResamplingContainer.h new file mode 100644 index 0000000..9d6af1e --- /dev/null +++ b/dsp/ResamplingContainer.h @@ -0,0 +1,357 @@ +// File: ResamplingContainer.h +// Created Date: Saturday December 16th 2023 +// Author: Steven Atkinson (steven@atkinson.mn) + +// A container for real-time resampling using a Lanczos anti-aliasing filter + +// This file originally came from the iPlug2 library and has been subsequently modified; +// the following license is copied as required from +// https://github.com/iPlug2/iPlug2/blob/40ebb560eba68f096221e99ef0ae826611fc2bda/LICENSE.txt +// ------------------------------------------------------------------------------------- + +/* +iPlug 2 C++ Plug-in Framework. + +Copyright (C) the iPlug 2 Developers. Portions copyright other contributors, see each source file for more information. + +Based on WDL-OL/iPlug by Oli Larkin (2011-2018), and the original iPlug v1 (2008) by John Schwartz / Cockos + +LICENSE: + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +1. This notice may not be removed or altered from any source distribution. + +iPlug 2 includes the following 3rd party libraries (see each license info): + +* Cockos WDL https://www.cockos.com/wdl +* NanoVG https://github.com/memononen/nanovg +* NanoSVG https://github.com/memononen/nanosvg +* MetalNanoVG https://github.com/ollix/MetalNanoVG +* RTAudio https://www.music.mcgill.ca/~gary/rtaudio +* RTMidi https://www.music.mcgill.ca/~gary/rtmidi +*/ +// ------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +// #include "IPlugPlatform.h" + +// #include "heapbuf.h" +#include "ptrlist.h" + +#include "_LanczosResampler.h" + +namespace dsp { + +/** A multi-channel real-time resampling container that can be used to resample + * audio processing to a specified sample rate for the situation where you have + * some arbitary DSP code that requires a specific sample rate, then back to + * the original external sample rate, encapsulating the arbitrary DSP code. + + * Three modes are supported: + * - Linear interpolation: simple linear interpolation between samples + * - Cubic interpolation: cubic interpolation between samples + * - Lanczos: Lanczos resampling uses an approximation of the sinc function to + * interpolate between samples. This is the highest quality resampling mode. + * + * The Lanczos resampler has a configurable filter size (A) that affects the + * latency of the resampler. It can also optionally use SIMD instructions to + * when T==float. + * + * + * @tparam T the sampletype + * @tparam NCHANS the number of channels + * @tparam A The Lanczos filter size for the LanczosResampler resampler mode + A higher value makes the filter closer to an + ideal stop-band that rejects high-frequency content (anti-aliasing), + but at the expense of higher latency + */ +template +class RealtimeResampler +{ +public: + using BlockProcessFunc = std::function; + using LanczosResampler = LanczosResampler; + + // :param renderingSampleRate: The sample rate required by the code to be encapsulated. + RealtimeResampler(double renderingSampleRate, ESRCMode mode = ESRCMode::kLinearInterpolation) + : mResamplingMode(mode) + , mRenderingSampleRate(renderingSampleRate) + { + } + + RealtimeResampler(const RealtimeResampler&) = delete; + RealtimeResampler& operator=(const RealtimeResampler&) = delete; + + void SetResamplingMode(ESRCMode mode) + { + mResamplingMode = mode; + Reset(mInputSampleRate); + } + + // :param inputSampleRate: The external sample rate interacting with this object. + // :param blockSize: The largest block size that will be given to this class to process until Reset() is called + // again. + void Reset(double inputSampleRate, int blockSize = DEFAULT_BLOCK_SIZE) + { + if (mInputSampleRate == inputSampleRate && mMaxBlockSize == blockSize) + { + ClearBuffers(); + return; + } + + mInputSampleRate = inputSampleRate; + mRatio1 = mInputSampleRate / mRenderingSampleRate; + mRatio2 = mRenderingSampleRate / mInputSampleRate; + // The buffers for the encapsulated code need to be long enough to hold the correesponding number of samples + mMaxBlockSize = blockSize; + mMaxEncapsulatedBlockSize = MaxEncapsulatedBlockSize(blockSize); + + mScratchExternalInputData.Resize(mMaxBlockSize * NCHANS); // This may contain junk right now. + mEncapsulatedInputData.Resize(mMaxEncapsulatedBlockSize * NCHANS); // This may contain junk right now. + mEncapsulatedOutputData.Resize(mMaxEncapsulatedBlockSize * NCHANS); // This may contain junk right now. + mScratchExternalInputPointers.Empty(); + mEncapsulatedInputPointers.Empty(); + mEncapsulatedOutputPointers.Empty(); + + for (auto chan=0; chan(mInputSampleRate, mRenderingSampleRate); + mResampler2 = std::make_unique(mRenderingSampleRate, mInputSampleRate); + + // Zeroes the scratch pointers so that we warm up with silence. + ClearBuffers(); + + // Warm up the resampling container with enough silence that the first real buffer can yield the required number + // of output samples. + const auto midSamples = mResampler2->GetNumSamplesRequiredFor(1); + mLatency = int(mResampler1->GetNumSamplesRequiredFor(midSamples)); + // 1. Push some silence through the first resampler. + // + mResampler1->PushBlock(mScratchExternalInputPointers.GetList(), mLatency); + const size_t populated = mResampler1->PopBlock(mEncapsulatedInputPointers.GetList(), midSamples); + if (populated < midSamples) { + throw std::runtime_error("Didn't get enough samples required for pre-population!"); + } + // 2. "process" the warm-up in the encapsulated DSP. + // Since this is an audio effect, we can assume that (1) it's causal and (2) that it's silent until + // a non-silent input is given to it. + // Therefore, we don't *acutally* need to use `func()`--we can assume that it would output silence! + // func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), (int)populated); + FallbackFunc(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), (int)populated); + mResampler2->PushBlock(mEncapsulatedOutputPointers.GetList(), populated); + // Now we're ready for the first "real" buffer. + } + else + { + mResampler1 = nullptr; + mResampler2 = nullptr; + mLatency = 0; + ClearBuffers(); // Takes care of that junk + } + } + + /** Resample an input block with a per-block function (up sample input -> process with function -> down sample) + * @param inputs Two-dimensional array containing the non-interleaved input buffers of audio samples for all channels + * @param outputs Two-dimensional array for audio output (non-interleaved). + * @param nFrames The block size for this block: number of samples per channel. + * @param func The function that processes the audio sample at the higher sampling rate. NOTE: std::function can call malloc if you pass in captures */ + void ProcessBlock(T** inputs, T** outputs, int nFrames, BlockProcessFunc func) + { + switch (mResamplingMode) + { + case ESRCMode::kLinearInterpolation: + { + // FIXME check this! + const auto nNewFrames = LinearInterpolate(inputs, mEncapsulatedInputPointers.GetList(), nFrames, mRatio1, mMaxEncapsulatedBlockSize); + func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), nNewFrames); + LinearInterpolate(mEncapsulatedOutputPointers.GetList(), outputs, nNewFrames, mRatio2, nFrames); + break; + } + case ESRCMode::kCubicInterpolation: + { + // FIXME check this! + const auto nNewFrames = CubicInterpolate(inputs, mEncapsulatedInputPointers.GetList(), nFrames, mRatio1, mMaxEncapsulatedBlockSize); + func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), nNewFrames); + CubicInterpolate(mEncapsulatedOutputPointers.GetList(), outputs, nNewFrames, mRatio2, nFrames); + break; + } + case ESRCMode::kLancsoz: + { + mResampler1->PushBlock(inputs, nFrames); + // This is the most samples the encapsualted context might get. Sometimes it'll get fewer. + const auto maxEncapsulatedLen = MaxEncapsulatedBlockSize(nFrames); + + // Process as much audio as you can with the encapsulated DSP, and push it into the second resampler. + // This will give the second reasmpler enough for it to pop the required buffer size to complete this function + // correctly. + while (mResampler1->GetNumSamplesRequiredFor(1) == 0) // i.e. there's more to process + { + // Get a block no larger than the encapsulated DSP is expecting. + const size_t populated1 = mResampler1->PopBlock(mEncapsulatedInputPointers.GetList(), maxEncapsulatedLen); + if (populated1 > maxEncapsulatedLen) { + throw std::runtime_error("Got more encapsulated samples than the encapsulated DSP is prepared to handle!"); + } + func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), (int)populated1); + // And push the results into the second resampler so that it has what the external context requires. + mResampler2->PushBlock(mEncapsulatedOutputPointers.GetList(), populated1); + } + + // Pop the required output from the second resampler for the external context. + const auto populated2 = mResampler2->PopBlock(outputs, nFrames); + if (populated2 < nFrames) { + throw std::runtime_error("Did not yield enough samples to provide the required output buffer!"); + } + // Get ready for the next block: + mResampler1->RenormalizePhases(); + mResampler2->RenormalizePhases(); + break; + } + default: + break; + } + } + + int GetLatency() const { return mLatency; } + +private: + static inline int LinearInterpolate(T** inputs, T** outputs, int inputLen, double ratio, int maxOutputLen) + { + // FIXME check through this! + const auto outputLen = + std::min(static_cast(std::ceil(static_cast(inputLen) / ratio)), maxOutputLen); + + for (auto writePos = 0; writePos < outputLen; writePos++) + { + const auto readPos = ratio * static_cast(writePos); + const auto readPostionTrunc = std::floor(readPos); + const auto readPosInt = static_cast(readPostionTrunc); + + if (readPosInt < inputLen) + { + const auto y = readPos - readPostionTrunc; + + for (auto chan=0; chan(std::ceil(static_cast(inputLen) / ratio)), maxOutputLen); + + for (auto writePos = 0; writePos < outputLen; writePos++) + { + const auto readPos = ratio * static_cast(writePos); + const auto readPostionTrunc = std::floor(readPos); + const auto readPosInt = static_cast(readPostionTrunc); + + if (readPosInt < inputLen) + { + const auto y = readPos - readPostionTrunc; + + for (auto chan=0; chan 0) ? inputs[chan][readPosInt - 1] : 0.0f; + const auto x0 = ((readPosInt) < inputLen) ? inputs[chan][readPosInt] : inputs[chan][readPosInt-1]; + const auto x1 = ((readPosInt + 1) < inputLen) ? inputs[chan][readPosInt + 1] : inputs[chan][readPosInt-1]; + const auto x2 = ((readPosInt + 2) < inputLen) ? inputs[chan][readPosInt + 2] : inputs[chan][readPosInt-1]; + + const auto c = (x1 - xm1) * 0.5; + const auto v = x0 - x1; + const auto w = c + v; + const auto a = w + v + (x2 - x0) * 0.5; + const auto b = w + a; + + outputs[chan][writePos] = ((((a * y) -b) * y + c) * y + x0); + } + } + } + + return outputLen; + } + + void ClearBuffers() + { + memset(mScratchExternalInputData.Get(), 0.0f, DataSize(mMaxBlockSize)); + const auto encapsulatedDataSize = DataSize(mMaxEncapsulatedBlockSize); + memset(mEncapsulatedInputData.Get(), 0.0f, encapsulatedDataSize); + memset(mEncapsulatedOutputData.Get(), 0.0f, encapsulatedDataSize); + + if (mResamplingMode == ESRCMode::kLancsoz) + { + if (mResampler1 != nullptr) { + mResampler1->ClearBuffer(); + } + if (mResampler2 != nullptr) { + mResampler2->ClearBuffer(); + } + } + } + + // How big could the corresponding encapsulated buffer be for a buffer at the external sample rate of a given size? + int MaxEncapsulatedBlockSize(const int externalBlockSize) const { + return static_cast(std::ceil(static_cast(externalBlockSize) / mRatio1)); + } + + // Size of the multi-channel data for a given block size + size_t DataSize(const int blockSize) const { return blockSize * NCHANS * sizeof(T);}; + + void FallbackFunc(T** inputs, T** outputs, int n) { + for (int i=0; i mScratchExternalInputData; + WDL_PtrList mScratchExternalInputPointers; + // Buffers for the input & output to the encapsulated DSP + WDL_TypedBuf mEncapsulatedInputData; + WDL_PtrList mEncapsulatedInputPointers; + WDL_TypedBuf mEncapsulatedOutputData; + WDL_PtrList mEncapsulatedOutputPointers; + // Sample rate ratio from external to encapsulated, from encapsulated to external. + double mRatio1 = 0.0, mRatio2 = 0.0; + // Sample rate of the external context. + double mInputSampleRate = 0.0; + // The size of the largest block the external context may provide. (It might provide something smaller.) + int mMaxBlockSize = 0; + // The size of the largest possible encapsulated block + int mMaxEncapsulatedBlockSize = 0; + // How much latency this object adds due to both of its resamplers. This does _not_ include the latency due to the + // encapsulated `func()`. + int mLatency = 0; + // The sample rate required by the DSP that this object encapsulates + const double mRenderingSampleRate; + ESRCMode mResamplingMode; + // Pair of resamplers for (1) external -> encapsulated, (2) encapsulated -> external + std::unique_ptr mResampler1, mResampler2; +}; + +}; // namespace dsp diff --git a/dsp/_LanczosResampler.h b/dsp/_LanczosResampler.h new file mode 100644 index 0000000..53a3b77 --- /dev/null +++ b/dsp/_LanczosResampler.h @@ -0,0 +1,369 @@ +// File: _LanczosResampler.h +// Created Date: Saturday December 16th 2023 +// Author: Steven Atkinson (steven@atkinson.mn) + + +// This file originally came from the iPlug2 library and has been subsequently modified; +// the following license is copied as required from +// https://github.com/iPlug2/iPlug2/blob/40ebb560eba68f096221e99ef0ae826611fc2bda/LICENSE.txt +// ------------------------------------------------------------------------------------- + +/* +iPlug 2 C++ Plug-in Framework. + +Copyright (C) the iPlug 2 Developers. Portions copyright other contributors, see each source file for more information. + +Based on WDL-OL/iPlug by Oli Larkin (2011-2018), and the original iPlug v1 (2008) by John Schwartz / Cockos + +LICENSE: + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +1. This notice may not be removed or altered from any source distribution. + +iPlug 2 includes the following 3rd party libraries (see each license info): + +* Cockos WDL https://www.cockos.com/wdl +* NanoVG https://github.com/memononen/nanovg +* NanoSVG https://github.com/memononen/nanosvg +* MetalNanoVG https://github.com/ollix/MetalNanoVG +* RTAudio https://www.music.mcgill.ca/~gary/rtaudio +* RTMidi https://www.music.mcgill.ca/~gary/rtmidi +*/ +// ------------------------------------------------------------------------------------- + +/* + This code is derived from + https://github.com/surge-synthesizer/sst-basic-blocks/blob/main/include/sst/basic-blocks/dsp/LanczosResampler.h + + The following license info is copied from the above file: + + * sst-basic-blocks - an open source library of core audio utilities + * built by Surge Synth Team. + * + * Provides a collection of tools useful on the audio thread for blocks, + * modulation, etc... or useful for adapting code to multiple environments. + * + * Copyright 2023, various authors, as described in the GitHub + * transaction log. Parts of this code are derived from similar + * functions original in Surge or ShortCircuit. + * + * sst-basic-blocks is released under the GNU General Public Licence v3 + * or later (GPL-3.0-or-later). The license is found in the "LICENSE" + * file in the root of this repository, or at + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * All source in sst-basic-blocks available at + * https://github.com/surge-synthesizer/sst-basic-blocks + + * A special note on licensing: This file (and only this file) + * has Paul Walker (baconpaul) as the sole author to date. + * + * In order to make this handy small function based on public + * information available to a set of open source projects + * adapting hardware to software, but which are licensed under + * MIT or BSD or similar licenses, this file and only this file + * can be used in an MIT/BSD context as well as a GPL3 context, by + * copying it and modifying it as you see fit. + * + * If you do that, you will need to replace the `sum_ps_to_float` + * call below with either an hadd if you are SSE3 or higher or + * an appropriate reduction operator from your toolkit. + * + * But basically: Need to resample 48k to variable rate with + * a small window and want to use this? Go for it! + * + * For avoidance of doubt, this license exception only + * applies to this file. + */ + +#pragma once + +#include +#include +#include +#include + +#if defined AUDIODSPTOOLS_SIMDE + #if defined(__arm64__) + #define SIMDE_ENABLE_NATIVE_ALIASES + #include "simde/x86/sse2.h" + #else + #include + #endif +#endif + +// #include "IPlugConstants.h" + +namespace dsp +{ +/* LanczosResampler + * + * A class that implement Lanczos resampling, optionally using SIMD instructions. + * Define AUDIODSPTOOLS_SIMDE at project level in order to use SIMD and if on non-x86_64 + * include the SIMDE library in your search paths in order to translate intel + * intrinsics to e.g. arm64 + * + * See https://en.wikipedia.org/wiki/Lanczos_resampling + * + * @tparam T the sampletype + * @tparam NCHANS the number of channels + * @tparam A The Lanczos filter size. A higher value makes the filter closer to an + ideal stop-band that rejects high-frequency content (anti-aliasing), + but at the expense of higher latency + */ +template +class LanczosResampler +{ +private: +#if AUDIODSPTOOLS_SIMDE + static_assert(std::is_same::value, "LanczosResampler requires T to be float when using SIMD instructions"); + static_assert(false, "SIMD version has not been checked! You need to remove this to use it at your own risk!"); +#endif + + // The buffer size. This needs to be at least as large as the largest block of samples + // that the input side will see. + static constexpr size_t kBufferSize = 4096; + // The filter width. 2x because the filter goes from -A to A + static constexpr size_t kFilterWidth = A * 2; + // The discretization resolution for the filter table. + static constexpr size_t kTablePoints = 8192; + static constexpr double kDeltaX = 1.0 / (kTablePoints); + +public: + /** Constructor + * @param inputRate The input sample rate + * @param outputRate The output sample rate + */ + LanczosResampler(float inputRate, float outputRate) + : mInputSampleRate(inputRate) + , mOutputSamplerate(outputRate) + , mPhaseOutIncr(mInputSampleRate / mOutputSamplerate) + { + ClearBuffer(); + + auto kernel = [](double x) { + if (std::fabs(x) < 1e-7) + return T(1.0); + + const auto pi = iplug::PI; + return T(A * std::sin(pi * x) * std::sin(pi * x / A) / (pi * pi * x * x)); + }; + + if (!sTablesInitialized) + { + for (auto t = 0; t < kTablePoints + 1; ++t) + { + const double x0 = kDeltaX * t; + + for (auto i=0; i A + 1 + * + * Use the fact that mPhaseInIncr = mInputSampleRate and find + * res > (A+1) - (mPhaseIn - mPhaseOut + mPhaseOutIncr * desiredOutputs) * sri + */ + auto res = A + 1.0 - (mPhaseIn - mPhaseOut - mPhaseOutIncr * nOutputSamples); + + return static_cast(std::max(res + 1.0, 0.0)); + } + + inline void PushBlock(T** inputs, size_t nFrames) + { + for (auto s=0; s A + 1) + { + ReadSamples((mPhaseIn - mPhaseOut), outputs, populated); + mPhaseOut += mPhaseOutIncr; + populated++; + } + return populated; + } + + inline void RenormalizePhases() + { + mPhaseIn -= mPhaseOut; + mPhaseOut = 0; + } + + void Reset() + { + ClearBuffer(); + } + + void ClearBuffer() + { + memset(mInputBuffer, 0, NCHANS * kBufferSize * 2 * sizeof(T)); + } + +private: +#ifdef IPLUG_SIMDE + inline void ReadSamples(double xBack, T** outputs, int s) const + { + float bufferReadPosition = static_cast(mWritePos - xBack); + int bufferReadIndex = static_cast(std::floor(bufferReadPosition)); + float bufferFracPosition = 1.0f - (bufferReadPosition - static_cast(bufferReadIndex)); + + bufferReadIndex = (bufferReadIndex + kBufferSize) & (kBufferSize - 1); + bufferReadIndex += (bufferReadIndex <= static_cast(A)) * kBufferSize; + + float tablePosition = bufferFracPosition * kTablePoints; + int tableIndex = static_cast(tablePosition); + float tableFracPosition = (tablePosition - tableIndex); + + __m128 sum[NCHANS]; + for (auto & v : sum) { + v = _mm_setzero_ps(); // Initialize sum vectors to zero + } + + for (int i = 0; i < A; i += 4) // Process four samples at a time + { + // Load filter coefficients and input samples into SSE registers + __m128 f0 = _mm_load_ps(&sTable[tableIndex][i]); + __m128 df0 = _mm_load_ps(&sDeltaTable[tableIndex][i]); + __m128 f1 = _mm_load_ps(&sTable[tableIndex][A + i]); + __m128 df1 = _mm_load_ps(&sDeltaTable[tableIndex][A + i]); + + // Interpolate filter coefficients + __m128 tfp = _mm_set1_ps(tableFracPosition); + f0 = _mm_add_ps(f0, _mm_mul_ps(df0, tfp)); + f1 = _mm_add_ps(f1, _mm_mul_ps(df1, tfp)); + + for (int c = 0; c < NCHANS; c++) + { + // Load input data + __m128 d0 = _mm_set_ps(mInputBuffer[c][bufferReadIndex - A + i + 3], + mInputBuffer[c][bufferReadIndex - A + i + 2], + mInputBuffer[c][bufferReadIndex - A + i + 1], + mInputBuffer[c][bufferReadIndex - A + i]); + __m128 d1 = _mm_set_ps(mInputBuffer[c][bufferReadIndex + i + 3], + mInputBuffer[c][bufferReadIndex + i + 2], + mInputBuffer[c][bufferReadIndex + i + 1], + mInputBuffer[c][bufferReadIndex + i]); + + // Perform multiplication and accumulate + __m128 result0 = _mm_mul_ps(f0, d0); + __m128 result1 = _mm_mul_ps(f1, d1); + sum[c] = _mm_add_ps(sum[c], _mm_add_ps(result0, result1)); + } + } + + // Extract the final sums and store them in the output + for (int c = 0; c < NCHANS; c++) + { + float sumArray[4]; + _mm_store_ps(sumArray, sum[c]); + outputs[c][s] = sumArray[0] + sumArray[1] + sumArray[2] + sumArray[3]; + } + } +#else // scalar + inline void ReadSamples(double xBack, T** outputs, int s) const + { + double bufferReadPosition = mWritePos - xBack; + int bufferReadIndex = std::floor(bufferReadPosition); + double bufferFracPosition = 1.0 - (bufferReadPosition - bufferReadIndex); + + bufferReadIndex = (bufferReadIndex + kBufferSize) & (kBufferSize - 1); + bufferReadIndex += (bufferReadIndex <= static_cast(A)) * kBufferSize; + + double tablePosition = bufferFracPosition * kTablePoints; + int tableIndex = static_cast(tablePosition); + double tableFracPosition = (tablePosition - tableIndex); + + T sum[NCHANS] = {0.0}; + + for (auto i=0; i +T LanczosResampler::sTable alignas(16) [LanczosResampler::kTablePoints + 1][LanczosResampler::kFilterWidth]; + +template +T LanczosResampler::sDeltaTable alignas(16) [LanczosResampler::kTablePoints + 1][LanczosResampler::kFilterWidth]; + +template +bool LanczosResampler::sTablesInitialized{false}; + +} // namespace iplug + From 7eda256bcef52944eb79c5abd016afc9d4d8b949 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 1 Jan 2024 17:19:33 -0800 Subject: [PATCH 3/7] Move files --- .../Dependencies/LanczosResampler.h} | 0 .../ResamplingContainer/Dependencies}/WDL/heapbuf.h | 0 .../ResamplingContainer/Dependencies}/WDL/ptrlist.h | 0 .../ResamplingContainer/Dependencies}/WDL/wdltypes.h | 0 dsp/{ => ResamplingContainer}/ResamplingContainer.h | 12 ++++++------ 5 files changed, 6 insertions(+), 6 deletions(-) rename dsp/{_LanczosResampler.h => ResamplingContainer/Dependencies/LanczosResampler.h} (100%) rename {Dependencies => dsp/ResamplingContainer/Dependencies}/WDL/heapbuf.h (100%) rename {Dependencies => dsp/ResamplingContainer/Dependencies}/WDL/ptrlist.h (100%) rename {Dependencies => dsp/ResamplingContainer/Dependencies}/WDL/wdltypes.h (100%) rename dsp/{ => ResamplingContainer}/ResamplingContainer.h (97%) diff --git a/dsp/_LanczosResampler.h b/dsp/ResamplingContainer/Dependencies/LanczosResampler.h similarity index 100% rename from dsp/_LanczosResampler.h rename to dsp/ResamplingContainer/Dependencies/LanczosResampler.h diff --git a/Dependencies/WDL/heapbuf.h b/dsp/ResamplingContainer/Dependencies/WDL/heapbuf.h similarity index 100% rename from Dependencies/WDL/heapbuf.h rename to dsp/ResamplingContainer/Dependencies/WDL/heapbuf.h diff --git a/Dependencies/WDL/ptrlist.h b/dsp/ResamplingContainer/Dependencies/WDL/ptrlist.h similarity index 100% rename from Dependencies/WDL/ptrlist.h rename to dsp/ResamplingContainer/Dependencies/WDL/ptrlist.h diff --git a/Dependencies/WDL/wdltypes.h b/dsp/ResamplingContainer/Dependencies/WDL/wdltypes.h similarity index 100% rename from Dependencies/WDL/wdltypes.h rename to dsp/ResamplingContainer/Dependencies/WDL/wdltypes.h diff --git a/dsp/ResamplingContainer.h b/dsp/ResamplingContainer/ResamplingContainer.h similarity index 97% rename from dsp/ResamplingContainer.h rename to dsp/ResamplingContainer/ResamplingContainer.h index 9d6af1e..752b352 100644 --- a/dsp/ResamplingContainer.h +++ b/dsp/ResamplingContainer/ResamplingContainer.h @@ -46,9 +46,9 @@ iPlug 2 includes the following 3rd party libraries (see each license info): // #include "IPlugPlatform.h" // #include "heapbuf.h" -#include "ptrlist.h" +#include "Dependencies/WDL/ptrlist.h" -#include "_LanczosResampler.h" +#include "Dependencies/LanczosResampler.h" namespace dsp { @@ -76,21 +76,21 @@ namespace dsp { but at the expense of higher latency */ template -class RealtimeResampler +class ResamplingContainer { public: using BlockProcessFunc = std::function; using LanczosResampler = LanczosResampler; // :param renderingSampleRate: The sample rate required by the code to be encapsulated. - RealtimeResampler(double renderingSampleRate, ESRCMode mode = ESRCMode::kLinearInterpolation) + ResamplingContainer(double renderingSampleRate, ESRCMode mode = ESRCMode::kLinearInterpolation) : mResamplingMode(mode) , mRenderingSampleRate(renderingSampleRate) { } - RealtimeResampler(const RealtimeResampler&) = delete; - RealtimeResampler& operator=(const RealtimeResampler&) = delete; + ResamplingContainer(const ResamplingContainer&) = delete; + ResamplingContainer& operator=(const ResamplingContainer&) = delete; void SetResamplingMode(ESRCMode mode) { From 45358283703b40dd51059e12592a9e50e3a60394 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 1 Jan 2024 17:33:21 -0800 Subject: [PATCH 4/7] Remove non-Lanczos code --- dsp/ResamplingContainer/ResamplingContainer.h | 48 +------------------ 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/dsp/ResamplingContainer/ResamplingContainer.h b/dsp/ResamplingContainer/ResamplingContainer.h index 752b352..eeecd47 100644 --- a/dsp/ResamplingContainer/ResamplingContainer.h +++ b/dsp/ResamplingContainer/ResamplingContainer.h @@ -83,20 +83,13 @@ class ResamplingContainer using LanczosResampler = LanczosResampler; // :param renderingSampleRate: The sample rate required by the code to be encapsulated. - ResamplingContainer(double renderingSampleRate, ESRCMode mode = ESRCMode::kLinearInterpolation) - : mResamplingMode(mode) - , mRenderingSampleRate(renderingSampleRate) + ResamplingContainer(double renderingSampleRate) + : mRenderingSampleRate(renderingSampleRate) { } ResamplingContainer(const ResamplingContainer&) = delete; ResamplingContainer& operator=(const ResamplingContainer&) = delete; - - void SetResamplingMode(ESRCMode mode) - { - mResamplingMode = mode; - Reset(mInputSampleRate); - } // :param inputSampleRate: The external sample rate interacting with this object. // :param blockSize: The largest block size that will be given to this class to process until Reset() is called @@ -130,7 +123,6 @@ class ResamplingContainer mEncapsulatedOutputPointers.Add(mEncapsulatedOutputData.Get() + (chan * mMaxEncapsulatedBlockSize)); } - if (mResamplingMode == ESRCMode::kLancsoz) { mResampler1 = std::make_unique(mInputSampleRate, mRenderingSampleRate); mResampler2 = std::make_unique(mRenderingSampleRate, mInputSampleRate); @@ -158,13 +150,6 @@ class ResamplingContainer mResampler2->PushBlock(mEncapsulatedOutputPointers.GetList(), populated); // Now we're ready for the first "real" buffer. } - else - { - mResampler1 = nullptr; - mResampler2 = nullptr; - mLatency = 0; - ClearBuffers(); // Takes care of that junk - } } /** Resample an input block with a per-block function (up sample input -> process with function -> down sample) @@ -174,26 +159,6 @@ class ResamplingContainer * @param func The function that processes the audio sample at the higher sampling rate. NOTE: std::function can call malloc if you pass in captures */ void ProcessBlock(T** inputs, T** outputs, int nFrames, BlockProcessFunc func) { - switch (mResamplingMode) - { - case ESRCMode::kLinearInterpolation: - { - // FIXME check this! - const auto nNewFrames = LinearInterpolate(inputs, mEncapsulatedInputPointers.GetList(), nFrames, mRatio1, mMaxEncapsulatedBlockSize); - func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), nNewFrames); - LinearInterpolate(mEncapsulatedOutputPointers.GetList(), outputs, nNewFrames, mRatio2, nFrames); - break; - } - case ESRCMode::kCubicInterpolation: - { - // FIXME check this! - const auto nNewFrames = CubicInterpolate(inputs, mEncapsulatedInputPointers.GetList(), nFrames, mRatio1, mMaxEncapsulatedBlockSize); - func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), nNewFrames); - CubicInterpolate(mEncapsulatedOutputPointers.GetList(), outputs, nNewFrames, mRatio2, nFrames); - break; - } - case ESRCMode::kLancsoz: - { mResampler1->PushBlock(inputs, nFrames); // This is the most samples the encapsualted context might get. Sometimes it'll get fewer. const auto maxEncapsulatedLen = MaxEncapsulatedBlockSize(nFrames); @@ -221,11 +186,6 @@ class ResamplingContainer // Get ready for the next block: mResampler1->RenormalizePhases(); mResampler2->RenormalizePhases(); - break; - } - default: - break; - } } int GetLatency() const { return mLatency; } @@ -303,15 +263,12 @@ class ResamplingContainer memset(mEncapsulatedInputData.Get(), 0.0f, encapsulatedDataSize); memset(mEncapsulatedOutputData.Get(), 0.0f, encapsulatedDataSize); - if (mResamplingMode == ESRCMode::kLancsoz) - { if (mResampler1 != nullptr) { mResampler1->ClearBuffer(); } if (mResampler2 != nullptr) { mResampler2->ClearBuffer(); } - } } // How big could the corresponding encapsulated buffer be for a buffer at the external sample rate of a given size? @@ -349,7 +306,6 @@ class ResamplingContainer int mLatency = 0; // The sample rate required by the DSP that this object encapsulates const double mRenderingSampleRate; - ESRCMode mResamplingMode; // Pair of resamplers for (1) external -> encapsulated, (2) encapsulated -> external std::unique_ptr mResampler1, mResampler2; }; From ab4686a9de0dbeddc48e9cfb0e54dccb0fc1969b Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 1 Jan 2024 17:34:47 -0800 Subject: [PATCH 5/7] Fix file header --- dsp/ResamplingContainer/Dependencies/LanczosResampler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/ResamplingContainer/Dependencies/LanczosResampler.h b/dsp/ResamplingContainer/Dependencies/LanczosResampler.h index 53a3b77..5ac52b1 100644 --- a/dsp/ResamplingContainer/Dependencies/LanczosResampler.h +++ b/dsp/ResamplingContainer/Dependencies/LanczosResampler.h @@ -1,4 +1,4 @@ -// File: _LanczosResampler.h +// File: LanczosResampler.h // Created Date: Saturday December 16th 2023 // Author: Steven Atkinson (steven@atkinson.mn) From 1f996006c881f9585e0498f260ad2d23c185ed3b Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Mon, 1 Jan 2024 17:38:22 -0800 Subject: [PATCH 6/7] Formatting --- .../Dependencies/LanczosResampler.h | 144 ++-- .../Dependencies/WDL/heapbuf.h | 613 ++++++++++-------- .../Dependencies/WDL/ptrlist.h | 382 ++++++----- .../Dependencies/WDL/wdltypes.h | 259 ++++---- dsp/ResamplingContainer/ResamplingContainer.h | 189 +++--- 5 files changed, 849 insertions(+), 738 deletions(-) diff --git a/dsp/ResamplingContainer/Dependencies/LanczosResampler.h b/dsp/ResamplingContainer/Dependencies/LanczosResampler.h index 5ac52b1..f734cdf 100644 --- a/dsp/ResamplingContainer/Dependencies/LanczosResampler.h +++ b/dsp/ResamplingContainer/Dependencies/LanczosResampler.h @@ -9,7 +9,7 @@ // ------------------------------------------------------------------------------------- /* -iPlug 2 C++ Plug-in Framework. +iPlug 2 C++ Plug-in Framework. Copyright (C) the iPlug 2 Developers. Portions copyright other contributors, see each source file for more information. @@ -17,19 +17,24 @@ Based on WDL-OL/iPlug by Oli Larkin (2011-2018), and the original iPlug v1 (2008 LICENSE: -This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable +for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it +and redistribute it freely, subject to the following restrictions: -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If +you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not +required. +1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original +software. 1. This notice may not be removed or altered from any source distribution. iPlug 2 includes the following 3rd party libraries (see each license info): * Cockos WDL https://www.cockos.com/wdl * NanoVG https://github.com/memononen/nanovg -* NanoSVG https://github.com/memononen/nanosvg +* NanoSVG https://github.com/memononen/nanosvg * MetalNanoVG https://github.com/ollix/MetalNanoVG * RTAudio https://www.music.mcgill.ca/~gary/rtaudio * RTMidi https://www.music.mcgill.ca/~gary/rtmidi @@ -39,9 +44,9 @@ iPlug 2 includes the following 3rd party libraries (see each license info): /* This code is derived from https://github.com/surge-synthesizer/sst-basic-blocks/blob/main/include/sst/basic-blocks/dsp/LanczosResampler.h - + The following license info is copied from the above file: - + * sst-basic-blocks - an open source library of core audio utilities * built by Surge Synth Team. * @@ -112,11 +117,11 @@ namespace dsp * * @tparam T the sampletype * @tparam NCHANS the number of channels - * @tparam A The Lanczos filter size. A higher value makes the filter closer to an - ideal stop-band that rejects high-frequency content (anti-aliasing), + * @tparam A The Lanczos filter size. A higher value makes the filter closer to an + ideal stop-band that rejects high-frequency content (anti-aliasing), but at the expense of higher latency */ -template +template class LanczosResampler { private: @@ -136,46 +141,46 @@ class LanczosResampler public: /** Constructor - * @param inputRate The input sample rate - * @param outputRate The output sample rate - */ + * @param inputRate The input sample rate + * @param outputRate The output sample rate + */ LanczosResampler(float inputRate, float outputRate) : mInputSampleRate(inputRate) , mOutputSamplerate(outputRate) , mPhaseOutIncr(mInputSampleRate / mOutputSamplerate) { ClearBuffer(); - + auto kernel = [](double x) { if (std::fabs(x) < 1e-7) return T(1.0); - + const auto pi = iplug::PI; return T(A * std::sin(pi * x) * std::sin(pi * x / A) / (pi * pi * x * x)); }; - + if (!sTablesInitialized) { for (auto t = 0; t < kTablePoints + 1; ++t) { const double x0 = kDeltaX * t; - - for (auto i=0; i (A+1) - (mPhaseIn - mPhaseOut + mPhaseOutIncr * desiredOutputs) * sri */ auto res = A + 1.0 - (mPhaseIn - mPhaseOut - mPhaseOutIncr * nOutputSamples); - + return static_cast(std::max(res + 1.0, 0.0)); } - + inline void PushBlock(T** inputs, size_t nFrames) { - for (auto s=0; s(mWritePos - xBack); int bufferReadIndex = static_cast(std::floor(bufferReadPosition)); float bufferFracPosition = 1.0f - (bufferReadPosition - static_cast(bufferReadIndex)); - + bufferReadIndex = (bufferReadIndex + kBufferSize) & (kBufferSize - 1); bufferReadIndex += (bufferReadIndex <= static_cast(A)) * kBufferSize; - + float tablePosition = bufferFracPosition * kTablePoints; int tableIndex = static_cast(tablePosition); float tableFracPosition = (tablePosition - tableIndex); - + __m128 sum[NCHANS]; - for (auto & v : sum) { + for (auto& v : sum) + { v = _mm_setzero_ps(); // Initialize sum vectors to zero } - + for (int i = 0; i < A; i += 4) // Process four samples at a time { // Load filter coefficients and input samples into SSE registers @@ -267,31 +267,28 @@ class LanczosResampler __m128 df0 = _mm_load_ps(&sDeltaTable[tableIndex][i]); __m128 f1 = _mm_load_ps(&sTable[tableIndex][A + i]); __m128 df1 = _mm_load_ps(&sDeltaTable[tableIndex][A + i]); - + // Interpolate filter coefficients __m128 tfp = _mm_set1_ps(tableFracPosition); f0 = _mm_add_ps(f0, _mm_mul_ps(df0, tfp)); f1 = _mm_add_ps(f1, _mm_mul_ps(df1, tfp)); - + for (int c = 0; c < NCHANS; c++) { // Load input data - __m128 d0 = _mm_set_ps(mInputBuffer[c][bufferReadIndex - A + i + 3], - mInputBuffer[c][bufferReadIndex - A + i + 2], - mInputBuffer[c][bufferReadIndex - A + i + 1], - mInputBuffer[c][bufferReadIndex - A + i]); - __m128 d1 = _mm_set_ps(mInputBuffer[c][bufferReadIndex + i + 3], - mInputBuffer[c][bufferReadIndex + i + 2], - mInputBuffer[c][bufferReadIndex + i + 1], - mInputBuffer[c][bufferReadIndex + i]); - + __m128 d0 = + _mm_set_ps(mInputBuffer[c][bufferReadIndex - A + i + 3], mInputBuffer[c][bufferReadIndex - A + i + 2], + mInputBuffer[c][bufferReadIndex - A + i + 1], mInputBuffer[c][bufferReadIndex - A + i]); + __m128 d1 = _mm_set_ps(mInputBuffer[c][bufferReadIndex + i + 3], mInputBuffer[c][bufferReadIndex + i + 2], + mInputBuffer[c][bufferReadIndex + i + 1], mInputBuffer[c][bufferReadIndex + i]); + // Perform multiplication and accumulate __m128 result0 = _mm_mul_ps(f0, d0); __m128 result1 = _mm_mul_ps(f1, d1); sum[c] = _mm_add_ps(sum[c], _mm_add_ps(result0, result1)); } } - + // Extract the final sums and store them in the output for (int c = 0; c < NCHANS; c++) { @@ -316,17 +313,17 @@ class LanczosResampler T sum[NCHANS] = {0.0}; - for (auto i=0; i -T LanczosResampler::sTable alignas(16) [LanczosResampler::kTablePoints + 1][LanczosResampler::kFilterWidth]; +template +T LanczosResampler::sTable alignas( + 16)[LanczosResampler::kTablePoints + 1][LanczosResampler::kFilterWidth]; -template -T LanczosResampler::sDeltaTable alignas(16) [LanczosResampler::kTablePoints + 1][LanczosResampler::kFilterWidth]; +template +T LanczosResampler::sDeltaTable alignas( + 16)[LanczosResampler::kTablePoints + 1][LanczosResampler::kFilterWidth]; -template +template bool LanczosResampler::sTablesInitialized{false}; -} // namespace iplug - +} // namespace dsp diff --git a/dsp/ResamplingContainer/Dependencies/WDL/heapbuf.h b/dsp/ResamplingContainer/Dependencies/WDL/heapbuf.h index bf2a632..4343499 100644 --- a/dsp/ResamplingContainer/Dependencies/WDL/heapbuf.h +++ b/dsp/ResamplingContainer/Dependencies/WDL/heapbuf.h @@ -17,17 +17,17 @@ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - + */ /* - This file provides the interface and implementation for WDL_HeapBuf, a simple + This file provides the interface and implementation for WDL_HeapBuf, a simple malloc() wrapper for resizeable blocks. - Also in this file is WDL_TypedBuf which is a templated version WDL_HeapBuf + Also in this file is WDL_TypedBuf which is a templated version WDL_HeapBuf that manages type and type-size. - + */ #ifndef _WDL_HEAPBUF_H_ @@ -35,347 +35,392 @@ #ifndef WDL_HEAPBUF_IMPL_ONLY -#ifdef WDL_HEAPBUF_TRACE -#include -#define WDL_HEAPBUF_TRACEPARM(x) ,(x) -#else -#define WDL_HEAPBUF_TRACEPARM(x) -#endif + #ifdef WDL_HEAPBUF_TRACE + #include + #define WDL_HEAPBUF_TRACEPARM(x) , (x) + #else + #define WDL_HEAPBUF_TRACEPARM(x) + #endif -#include "wdltypes.h" + #include "wdltypes.h" class WDL_HeapBuf { - public: - // interface -#ifdef WDL_HEAPBUF_INTF_ONLY - void *Resize(int newsize, bool resizedown=true); - void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false); -#endif - void *Get() const { return m_size?m_buf:NULL; } // returns NULL if size is 0 - void *GetFast() const { return m_buf; } // returns last buffer if size is 0 - int GetSize() const { return m_size; } - void *GetAligned(int align) const { return (void *)(((UINT_PTR)Get() + (align-1)) & ~(UINT_PTR)(align-1)); } - - void SetGranul(int granul) { m_granul = granul; } - int GetGranul() const { return m_granul; } - - void *ResizeOK(int newsize, bool resizedown = true) { void *p=Resize(newsize, resizedown); return GetSize() == newsize ? p : NULL; } - - WDL_HeapBuf(const WDL_HeapBuf &cp) - { - m_buf=0; - CopyFrom(&cp,true); - } - WDL_HeapBuf &operator=(const WDL_HeapBuf &cp) - { - CopyFrom(&cp,false); - return *this; - } - +public: + // interface + #ifdef WDL_HEAPBUF_INTF_ONLY + void* Resize(int newsize, bool resizedown = true); + void CopyFrom(const WDL_HeapBuf* hb, bool exactCopyOfConfig = false); + #endif + void* Get() const { return m_size ? m_buf : NULL; } // returns NULL if size is 0 + void* GetFast() const { return m_buf; } // returns last buffer if size is 0 + int GetSize() const { return m_size; } + void* GetAligned(int align) const { return (void*)(((UINT_PTR)Get() + (align - 1)) & ~(UINT_PTR)(align - 1)); } + + void SetGranul(int granul) { m_granul = granul; } + int GetGranul() const { return m_granul; } + + void* ResizeOK(int newsize, bool resizedown = true) + { + void* p = Resize(newsize, resizedown); + return GetSize() == newsize ? p : NULL; + } + + WDL_HeapBuf(const WDL_HeapBuf& cp) + { + m_buf = 0; + CopyFrom(&cp, true); + } + WDL_HeapBuf& operator=(const WDL_HeapBuf& cp) + { + CopyFrom(&cp, false); + return *this; + } #ifndef WDL_HEAPBUF_TRACE - explicit WDL_HeapBuf(int granul=4096) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) - { - } - ~WDL_HeapBuf() - { - free(m_buf); - } + explicit WDL_HeapBuf(int granul = 4096) + : m_buf(NULL) + , m_alloc(0) + , m_size(0) + , m_granul(granul) + { + } + ~WDL_HeapBuf() { free(m_buf); } #else - explicit WDL_HeapBuf(int granul=4096, const char *tracetype="WDL_HeapBuf" - ) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) - { - m_tracetype = tracetype; - wdl_log("WDL_HeapBuf: created type: %s granul=%d\n",tracetype,granul); - } - ~WDL_HeapBuf() - { - wdl_log("WDL_HeapBuf: destroying type: %s (alloc=%d, size=%d)\n",m_tracetype,m_alloc,m_size); - free(m_buf); - } + explicit WDL_HeapBuf(int granul = 4096, const char* tracetype = "WDL_HeapBuf") + : m_buf(NULL) + , m_alloc(0) + , m_size(0) + , m_granul(granul) + { + m_tracetype = tracetype; + wdl_log("WDL_HeapBuf: created type: %s granul=%d\n", tracetype, granul); + } + ~WDL_HeapBuf() + { + wdl_log("WDL_HeapBuf: destroying type: %s (alloc=%d, size=%d)\n", m_tracetype, m_alloc, m_size); + free(m_buf); + } #endif #endif // !WDL_HEAPBUF_IMPL_ONLY - // implementation bits + // implementation bits #ifndef WDL_HEAPBUF_INTF_ONLY - #ifdef WDL_HEAPBUF_IMPL_ONLY - void *WDL_HeapBuf::Resize(int newsize, bool resizedown) - #else - void *Resize(int newsize, bool resizedown=true) + #ifdef WDL_HEAPBUF_IMPL_ONLY + void* WDL_HeapBuf::Resize(int newsize, bool resizedown) + #else + void* Resize(int newsize, bool resizedown = true) + #endif + { + if (newsize < 0) + newsize = 0; + #ifdef DEBUG_TIGHT_ALLOC // horribly slow, do not use for release builds + if (newsize == m_size) + return m_buf; + + int a = newsize; + if (a > m_size) + a = m_size; + void* newbuf = newsize ? malloc(newsize) : 0; + if (!newbuf && newsize) + { + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + WDL_HEAPBUF_ONMALLOCFAIL(newsize) #endif + return m_buf; + } + if (newbuf && m_buf) + memcpy(newbuf, m_buf, a); + m_size = m_alloc = newsize; + free(m_buf); + return m_buf = newbuf; + #endif + + if (newsize != m_size || (resizedown && newsize < m_alloc / 2)) + { + int resizedown_under = 0; + if (resizedown && newsize < m_size) + { + // shrinking buffer: only shrink if allocation decreases to min(alloc/2, alloc-granul*4) or 0 + resizedown_under = m_alloc - (m_granul << 2); + if (resizedown_under > m_alloc / 2) + resizedown_under = m_alloc / 2; + if (resizedown_under < 1) + resizedown_under = 1; + } + + if (newsize > m_alloc || newsize < resizedown_under) { - if (newsize<0) newsize=0; - #ifdef DEBUG_TIGHT_ALLOC // horribly slow, do not use for release builds - if (newsize == m_size) return m_buf; - - int a = newsize; - if (a > m_size) a=m_size; - void *newbuf = newsize ? malloc(newsize) : 0; - if (!newbuf && newsize) + int granul = newsize / 2; + int newalloc; + if (granul < m_granul) + granul = m_granul; + + if (newsize < 1) + newalloc = 0; + else if (m_granul < 4096) + newalloc = newsize + granul; + else + { + granul &= ~4095; + if (granul < 4096) + granul = 4096; + else if (granul > 4 * 1024 * 1024) + granul = 4 * 1024 * 1024; + newalloc = ((newsize + granul + 96) & ~4095) - 96; + } + + if (newalloc != m_alloc) + { + + #ifdef WDL_HEAPBUF_TRACE + wdl_log("WDL_HeapBuf: type %s realloc(%d) from %d\n", m_tracetype, newalloc, m_alloc); + #endif + if (newalloc <= 0) { - #ifdef WDL_HEAPBUF_ONMALLOCFAIL - WDL_HEAPBUF_ONMALLOCFAIL(newsize) - #endif - return m_buf; + free(m_buf); + m_buf = 0; + m_alloc = 0; + m_size = 0; + return 0; } - if (newbuf&&m_buf) memcpy(newbuf,m_buf,a); - m_size=m_alloc=newsize; - free(m_buf); - return m_buf=newbuf; - #endif - - if (newsize!=m_size || (resizedown && newsize < m_alloc/2)) + void* nbuf = realloc(m_buf, newalloc); + if (!nbuf) { - int resizedown_under = 0; - if (resizedown && newsize < m_size) + if (!(nbuf = malloc(newalloc))) { - // shrinking buffer: only shrink if allocation decreases to min(alloc/2, alloc-granul*4) or 0 - resizedown_under = m_alloc - (m_granul<<2); - if (resizedown_under > m_alloc/2) resizedown_under = m_alloc/2; - if (resizedown_under < 1) resizedown_under=1; + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + WDL_HEAPBUF_ONMALLOCFAIL(newalloc); + #endif + return m_size ? m_buf : 0; // failed, do not resize } - - if (newsize > m_alloc || newsize < resizedown_under) + + if (m_buf) { - int granul=newsize/2; - int newalloc; - if (granul < m_granul) granul=m_granul; - - if (newsize<1) newalloc=0; - else if (m_granul<4096) newalloc=newsize+granul; - else - { - granul &= ~4095; - if (granul< 4096) granul=4096; - else if (granul>4*1024*1024) granul=4*1024*1024; - newalloc = ((newsize + granul + 96)&~4095)-96; - } - - if (newalloc != m_alloc) - { - - #ifdef WDL_HEAPBUF_TRACE - wdl_log("WDL_HeapBuf: type %s realloc(%d) from %d\n",m_tracetype,newalloc,m_alloc); - #endif - if (newalloc <= 0) - { - free(m_buf); - m_buf=0; - m_alloc=0; - m_size=0; - return 0; - } - void *nbuf=realloc(m_buf,newalloc); - if (!nbuf) - { - if (!(nbuf=malloc(newalloc))) - { - #ifdef WDL_HEAPBUF_ONMALLOCFAIL - WDL_HEAPBUF_ONMALLOCFAIL(newalloc); - #endif - return m_size?m_buf:0; // failed, do not resize - } - - if (m_buf) - { - int sz=newsize0) memcpy(nbuf,m_buf,sz); - free(m_buf); - } - } - - m_buf=nbuf; - m_alloc=newalloc; - } // alloc size change - } // need size up or down - m_size=newsize; - } // size change - return m_size?m_buf:0; - } + int sz = newsize < m_size ? newsize : m_size; + if (sz > 0) + memcpy(nbuf, m_buf, sz); + free(m_buf); + } + } - #ifdef WDL_HEAPBUF_IMPL_ONLY - void WDL_HeapBuf::CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig) - #else - void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false) - #endif + m_buf = nbuf; + m_alloc = newalloc; + } // alloc size change + } // need size up or down + m_size = newsize; + } // size change + return m_size ? m_buf : 0; + } + + #ifdef WDL_HEAPBUF_IMPL_ONLY + void WDL_HeapBuf::CopyFrom(const WDL_HeapBuf* hb, bool exactCopyOfConfig) + #else + void CopyFrom(const WDL_HeapBuf* hb, bool exactCopyOfConfig = false) + #endif + { + if (exactCopyOfConfig) // copy all settings + { + free(m_buf); + + #ifdef WDL_HEAPBUF_TRACE + m_tracetype = hb->m_tracetype; + #endif + m_granul = hb->m_granul; + + m_size = m_alloc = 0; + m_buf = hb->m_buf && hb->m_alloc > 0 ? malloc(m_alloc = hb->m_alloc) : NULL; + #ifdef WDL_HEAPBUF_ONMALLOCFAIL + if (!m_buf && m_alloc) { - if (exactCopyOfConfig) // copy all settings - { - free(m_buf); - - #ifdef WDL_HEAPBUF_TRACE - m_tracetype = hb->m_tracetype; - #endif - m_granul = hb->m_granul; - - m_size=m_alloc=0; - m_buf=hb->m_buf && hb->m_alloc>0 ? malloc(m_alloc = hb->m_alloc) : NULL; - #ifdef WDL_HEAPBUF_ONMALLOCFAIL - if (!m_buf && m_alloc) { WDL_HEAPBUF_ONMALLOCFAIL(m_alloc) } ; - #endif - if (m_buf) memcpy(m_buf,hb->m_buf,m_size = hb->m_size); - else m_alloc=0; - } - else // copy just the data + size - { - const int newsz=hb->GetSize(); - Resize(newsz,true); - if (GetSize()!=newsz) Resize(0); - else memcpy(Get(),hb->Get(),newsz); - } - } + WDL_HEAPBUF_ONMALLOCFAIL(m_alloc) + }; + #endif + if (m_buf) + memcpy(m_buf, hb->m_buf, m_size = hb->m_size); + else + m_alloc = 0; + } + else // copy just the data + size + { + const int newsz = hb->GetSize(); + Resize(newsz, true); + if (GetSize() != newsz) + Resize(0); + else + memcpy(Get(), hb->Get(), newsz); + } + } #endif // ! WDL_HEAPBUF_INTF_ONLY #ifndef WDL_HEAPBUF_IMPL_ONLY - private: - void *m_buf; - int m_alloc; - int m_size; - int m_granul; +private: + void* m_buf; + int m_alloc; + int m_size; + int m_granul; #if defined(_WIN64) || defined(__LP64__) - public: - int ___pad; // keep size 8 byte aligned +public: + int ___pad; // keep size 8 byte aligned #endif #ifdef WDL_HEAPBUF_TRACE - const char *m_tracetype; + const char* m_tracetype; #endif - }; -template class WDL_TypedBuf +template +class WDL_TypedBuf { - public: - PTRTYPE *Get() const { return (PTRTYPE *) m_hb.Get(); } - PTRTYPE *GetFast() const { return (PTRTYPE *) m_hb.GetFast(); } - int GetSize() const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE); } - int GetSizeBytes() const { return m_hb.GetSize(); } - - PTRTYPE *Resize(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.Resize(newsize*sizeof(PTRTYPE),resizedown); } - PTRTYPE *ResizeOK(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.ResizeOK(newsize*sizeof(PTRTYPE), resizedown); } - - void SetToZero() { memset(m_hb.Get(), 0, m_hb.GetSize()); } - - PTRTYPE *GetAligned(int align) const { return (PTRTYPE *) m_hb.GetAligned(align); } - - PTRTYPE *Add(PTRTYPE val) +public: + PTRTYPE* Get() const { return (PTRTYPE*)m_hb.Get(); } + PTRTYPE* GetFast() const { return (PTRTYPE*)m_hb.GetFast(); } + int GetSize() const { return m_hb.GetSize() / (unsigned int)sizeof(PTRTYPE); } + int GetSizeBytes() const { return m_hb.GetSize(); } + + PTRTYPE* Resize(int newsize, bool resizedown = true) + { + return (PTRTYPE*)m_hb.Resize(newsize * sizeof(PTRTYPE), resizedown); + } + PTRTYPE* ResizeOK(int newsize, bool resizedown = true) + { + return (PTRTYPE*)m_hb.ResizeOK(newsize * sizeof(PTRTYPE), resizedown); + } + + void SetToZero() { memset(m_hb.Get(), 0, m_hb.GetSize()); } + + PTRTYPE* GetAligned(int align) const { return (PTRTYPE*)m_hb.GetAligned(align); } + + PTRTYPE* Add(PTRTYPE val) + { + const int sz = GetSize(); + PTRTYPE* p = ResizeOK(sz + 1, false); + if (p) { - const int sz=GetSize(); - PTRTYPE* p=ResizeOK(sz+1,false); - if (p) - { - p[sz]=val; - return p+sz; - } - return NULL; + p[sz] = val; + return p + sz; } - PTRTYPE *Add(const PTRTYPE *buf, int bufsz) + return NULL; + } + PTRTYPE* Add(const PTRTYPE* buf, int bufsz) + { + if (bufsz > 0) { - if (bufsz>0) - { - const int sz=GetSize(); - PTRTYPE* p=ResizeOK(sz+bufsz,false); - if (p) - { - p+=sz; - if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); - else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); - return p; - } - } - return NULL; - } - PTRTYPE *Set(const PTRTYPE *buf, int bufsz) - { - if (bufsz>=0) + const int sz = GetSize(); + PTRTYPE* p = ResizeOK(sz + bufsz, false); + if (p) { - PTRTYPE* p=ResizeOK(bufsz,false); - if (p) - { - if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); - else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); - return p; - } + p += sz; + if (buf) + memcpy(p, buf, bufsz * sizeof(PTRTYPE)); + else + memset((char*)p, 0, bufsz * sizeof(PTRTYPE)); + return p; } - return NULL; } - PTRTYPE* Insert(PTRTYPE val, int idx) + return NULL; + } + PTRTYPE* Set(const PTRTYPE* buf, int bufsz) + { + if (bufsz >= 0) { - const int sz=GetSize(); - if (idx >= 0 && idx <= sz) + PTRTYPE* p = ResizeOK(bufsz, false); + if (p) { - PTRTYPE* p=ResizeOK(sz+1,false); - if (p) - { - memmove(p+idx+1, p+idx, (sz-idx)*sizeof(PTRTYPE)); - p[idx]=val; - return p+idx; - } + if (buf) + memcpy(p, buf, bufsz * sizeof(PTRTYPE)); + else + memset((char*)p, 0, bufsz * sizeof(PTRTYPE)); + return p; } - return NULL; } - - void Delete(int idx) + return NULL; + } + PTRTYPE* Insert(PTRTYPE val, int idx) + { + const int sz = GetSize(); + if (idx >= 0 && idx <= sz) { - PTRTYPE* p=Get(); - const int sz=GetSize(); - if (idx >= 0 && idx < sz) + PTRTYPE* p = ResizeOK(sz + 1, false); + if (p) { - memmove(p+idx, p+idx+1, (sz-idx-1)*sizeof(PTRTYPE)); - Resize(sz-1,false); + memmove(p + idx + 1, p + idx, (sz - idx) * sizeof(PTRTYPE)); + p[idx] = val; + return p + idx; } } - - void SetGranul(int gran) { m_hb.SetGranul(gran); } - - int Find(PTRTYPE val) const + return NULL; + } + + void Delete(int idx) + { + PTRTYPE* p = Get(); + const int sz = GetSize(); + if (idx >= 0 && idx < sz) { - const PTRTYPE* p=Get(); - const int sz=GetSize(); - int i; - for (i=0; i < sz; ++i) if (p[i] == val) return i; - return -1; + memmove(p + idx, p + idx + 1, (sz - idx - 1) * sizeof(PTRTYPE)); + Resize(sz - 1, false); } + } -#ifndef WDL_HEAPBUF_TRACE - explicit WDL_TypedBuf(int granul=4096) : m_hb(granul) { } -#else - explicit WDL_TypedBuf(int granul=4096, const char *tracetype="WDL_TypedBuf") : m_hb(granul WDL_HEAPBUF_TRACEPARM(tracetype)) { } -#endif - ~WDL_TypedBuf() - { - } + void SetGranul(int gran) { m_hb.SetGranul(gran); } - WDL_HeapBuf *GetHeapBuf() { return &m_hb; } - const WDL_HeapBuf *GetHeapBuf() const { return &m_hb; } + int Find(PTRTYPE val) const + { + const PTRTYPE* p = Get(); + const int sz = GetSize(); + int i; + for (i = 0; i < sz; ++i) + if (p[i] == val) + return i; + return -1; + } - int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to delete item. returns number deleted + #ifndef WDL_HEAPBUF_TRACE + explicit WDL_TypedBuf(int granul = 4096) + : m_hb(granul) + { + } + #else + explicit WDL_TypedBuf(int granul = 4096, const char* tracetype = "WDL_TypedBuf") + : m_hb(granul WDL_HEAPBUF_TRACEPARM(tracetype)) + { + } + #endif + ~WDL_TypedBuf() {} + + WDL_HeapBuf* GetHeapBuf() { return &m_hb; } + const WDL_HeapBuf* GetHeapBuf() const { return &m_hb; } + + int DeleteBatch(bool (*proc)(PTRTYPE* p, void* ctx), + void* ctx = NULL) // proc returns true to delete item. returns number deleted + { + const int sz = GetSize(); + int cnt = 0; + PTRTYPE *rd = Get(), *wr = rd; + for (int x = 0; x < sz; x++) { - const int sz = GetSize(); - int cnt=0; - PTRTYPE *rd = Get(), *wr = rd; - for (int x = 0; x < sz; x ++) + if (!proc(rd, ctx)) { - if (!proc(rd,ctx)) - { - if (rd != wr) *wr=*rd; - wr++; - cnt++; - } - rd++; + if (rd != wr) + *wr = *rd; + wr++; + cnt++; } - if (cnt < sz) Resize(cnt,false); - return sz - cnt; + rd++; } + if (cnt < sz) + Resize(cnt, false); + return sz - cnt; + } - private: - WDL_HeapBuf m_hb; +private: + WDL_HeapBuf m_hb; }; #endif // ! WDL_HEAPBUF_IMPL_ONLY diff --git a/dsp/ResamplingContainer/Dependencies/WDL/ptrlist.h b/dsp/ResamplingContainer/Dependencies/WDL/ptrlist.h index eec7a98..c5b402c 100644 --- a/dsp/ResamplingContainer/Dependencies/WDL/ptrlist.h +++ b/dsp/ResamplingContainer/Dependencies/WDL/ptrlist.h @@ -1,7 +1,7 @@ /* WDL - ptrlist.h Copyright (C) 2005 and later, Cockos Incorporated - + This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. @@ -17,7 +17,7 @@ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. - + */ /* @@ -28,7 +28,7 @@ Note: on certain compilers, instantiating with WDL_PtrList bla; will give a warning, since the template will create code for "delete (void *)x;" which isn't technically valid. Oh well. - + */ #ifndef _WDL_PTRLIST_H_ @@ -36,238 +36,264 @@ #include "heapbuf.h" -template class WDL_PtrList +template +class WDL_PtrList { - public: - explicit WDL_PtrList(int defgran=4096) : m_hb(defgran WDL_HEAPBUF_TRACEPARM("WDL_PtrList")) - { - } +public: + explicit WDL_PtrList(int defgran = 4096) + : m_hb(defgran WDL_HEAPBUF_TRACEPARM("WDL_PtrList")) + { + } - ~WDL_PtrList() - { - } + ~WDL_PtrList() {} - PTRTYPE **GetList() const { return (PTRTYPE**)m_hb.Get(); } - PTRTYPE *Get(INT_PTR index) const - { - PTRTYPE **list = (PTRTYPE**)m_hb.Get(); - if (list && (UINT_PTR)index < (UINT_PTR)(m_hb.GetSize()/sizeof(PTRTYPE *))) return list[index]; - return NULL; - } + PTRTYPE** GetList() const { return (PTRTYPE**)m_hb.Get(); } + PTRTYPE* Get(INT_PTR index) const + { + PTRTYPE** list = (PTRTYPE**)m_hb.Get(); + if (list && (UINT_PTR)index < (UINT_PTR)(m_hb.GetSize() / sizeof(PTRTYPE*))) + return list[index]; + return NULL; + } - int GetSize(void) const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE *); } + int GetSize(void) const { return m_hb.GetSize() / (unsigned int)sizeof(PTRTYPE*); } - int Find(const PTRTYPE *p) const + int Find(const PTRTYPE* p) const + { + if (p) { - if (p) - { - PTRTYPE **list=(PTRTYPE **)m_hb.Get(); - int x; - const int n = GetSize(); - for (x = 0; x < n; x ++) if (list[x] == p) return x; - } - return -1; + PTRTYPE** list = (PTRTYPE**)m_hb.Get(); + int x; + const int n = GetSize(); + for (x = 0; x < n; x++) + if (list[x] == p) + return x; } - int FindR(const PTRTYPE *p) const + return -1; + } + int FindR(const PTRTYPE* p) const + { + if (p) { - if (p) - { - PTRTYPE **list=(PTRTYPE **)m_hb.Get(); - int x = GetSize(); - while (--x >= 0) if (list[x] == p) return x; - } - return -1; + PTRTYPE** list = (PTRTYPE**)m_hb.Get(); + int x = GetSize(); + while (--x >= 0) + if (list[x] == p) + return x; } + return -1; + } - PTRTYPE *Add(PTRTYPE *item) + PTRTYPE* Add(PTRTYPE* item) + { + const int s = GetSize(); + PTRTYPE** list = (PTRTYPE**)m_hb.ResizeOK((s + 1) * (unsigned int)sizeof(PTRTYPE*), false); + if (list) { - const int s=GetSize(); - PTRTYPE **list=(PTRTYPE **)m_hb.ResizeOK((s+1)*(unsigned int)sizeof(PTRTYPE*),false); - if (list) - { - list[s]=item; - return item; - } - return NULL; + list[s] = item; + return item; } + return NULL; + } - PTRTYPE *Set(int index, PTRTYPE *item) - { - PTRTYPE **list=(PTRTYPE **)m_hb.Get(); - if (list && index >= 0 && index < GetSize()) return list[index]=item; - return NULL; - } + PTRTYPE* Set(int index, PTRTYPE* item) + { + PTRTYPE** list = (PTRTYPE**)m_hb.Get(); + if (list && index >= 0 && index < GetSize()) + return list[index] = item; + return NULL; + } - PTRTYPE *Insert(int index, PTRTYPE *item) - { - int s=GetSize(); - PTRTYPE **list = (PTRTYPE **)m_hb.ResizeOK((s+1)*(unsigned int)sizeof(PTRTYPE*),false); + PTRTYPE* Insert(int index, PTRTYPE* item) + { + int s = GetSize(); + PTRTYPE** list = (PTRTYPE**)m_hb.ResizeOK((s + 1) * (unsigned int)sizeof(PTRTYPE*), false); - if (!list) return item; + if (!list) + return item; - if (index<0) index=0; - - int x; - for (x = s; x > index; x --) list[x]=list[x-1]; - return (list[x] = item); - } - int FindSorted(const PTRTYPE *p, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) const - { - bool m; - int i = LowerBound(p,&m,compar); - return m ? i : -1; - } - PTRTYPE *InsertSorted(PTRTYPE *item, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) - { - bool m; - return Insert(LowerBound(item,&m,compar),item); - } + if (index < 0) + index = 0; - void Delete(int index) + int x; + for (x = s; x > index; x--) + list[x] = list[x - 1]; + return (list[x] = item); + } + int FindSorted(const PTRTYPE* p, int (*compar)(const PTRTYPE** a, const PTRTYPE** b)) const + { + bool m; + int i = LowerBound(p, &m, compar); + return m ? i : -1; + } + PTRTYPE* InsertSorted(PTRTYPE* item, int (*compar)(const PTRTYPE** a, const PTRTYPE** b)) + { + bool m; + return Insert(LowerBound(item, &m, compar), item); + } + + void Delete(int index) + { + PTRTYPE** list = GetList(); + int size = GetSize(); + if (list && index >= 0 && index < size) { - PTRTYPE **list=GetList(); - int size=GetSize(); - if (list && index >= 0 && index < size) - { - if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); - m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); - } + if (index < --size) + memmove(list + index, list + index + 1, (unsigned int)sizeof(PTRTYPE*) * (size - index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*), false); } - void Delete(int index, bool wantDelete, void (*delfunc)(void *)=NULL) + } + void Delete(int index, bool wantDelete, void (*delfunc)(void*) = NULL) + { + PTRTYPE** list = GetList(); + int size = GetSize(); + if (list && index >= 0 && index < size) { - PTRTYPE **list=GetList(); - int size=GetSize(); - if (list && index >= 0 && index < size) + if (wantDelete) { - if (wantDelete) - { - if (delfunc) delfunc(Get(index)); - else delete Get(index); - } - if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); - m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); + if (delfunc) + delfunc(Get(index)); + else + delete Get(index); } + if (index < --size) + memmove(list + index, list + index + 1, (unsigned int)sizeof(PTRTYPE*) * (size - index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*), false); } - void Delete(int index, void (*delfunc)(PTRTYPE *)) + } + void Delete(int index, void (*delfunc)(PTRTYPE*)) + { + PTRTYPE** list = GetList(); + int size = GetSize(); + if (list && index >= 0 && index < size) { - PTRTYPE **list=GetList(); - int size=GetSize(); - if (list && index >= 0 && index < size) - { - if (delfunc) delfunc(Get(index)); - if (index < --size) memmove(list+index,list+index+1,(unsigned int)sizeof(PTRTYPE *)*(size-index)); - m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*),false); - } + if (delfunc) + delfunc(Get(index)); + if (index < --size) + memmove(list + index, list + index + 1, (unsigned int)sizeof(PTRTYPE*) * (size - index)); + m_hb.Resize(size * (unsigned int)sizeof(PTRTYPE*), false); } - void DeletePtr(const PTRTYPE *p) { Delete(Find(p)); } - void DeletePtr(const PTRTYPE *p, bool wantDelete, void (*delfunc)(void *)=NULL) { Delete(Find(p),wantDelete,delfunc); } - void DeletePtr(const PTRTYPE *p, void (*delfunc)(PTRTYPE *)) { Delete(Find(p),delfunc); } + } + void DeletePtr(const PTRTYPE* p) { Delete(Find(p)); } + void DeletePtr(const PTRTYPE* p, bool wantDelete, void (*delfunc)(void*) = NULL) + { + Delete(Find(p), wantDelete, delfunc); + } + void DeletePtr(const PTRTYPE* p, void (*delfunc)(PTRTYPE*)) { Delete(Find(p), delfunc); } - void Empty() - { - m_hb.Resize(0,false); - } - void Empty(bool wantDelete, void (*delfunc)(void *)=NULL) + void Empty() { m_hb.Resize(0, false); } + void Empty(bool wantDelete, void (*delfunc)(void*) = NULL) + { + if (wantDelete) { - if (wantDelete) + int x; + for (x = GetSize() - 1; x >= 0; x--) { - int x; - for (x = GetSize()-1; x >= 0; x --) + PTRTYPE* p = Get(x); + if (p) { - PTRTYPE* p = Get(x); - if (p) - { - if (delfunc) delfunc(p); - else delete p; - } - m_hb.Resize(x*(unsigned int)sizeof(PTRTYPE *),false); + if (delfunc) + delfunc(p); + else + delete p; } + m_hb.Resize(x * (unsigned int)sizeof(PTRTYPE*), false); } - m_hb.Resize(0,false); } - void Empty(void (*delfunc)(PTRTYPE *)) + m_hb.Resize(0, false); + } + void Empty(void (*delfunc)(PTRTYPE*)) + { + int x; + for (x = GetSize() - 1; x >= 0; x--) { - int x; - for (x = GetSize()-1; x >= 0; x --) - { - PTRTYPE* p = Get(x); - if (delfunc && p) delfunc(p); - m_hb.Resize(x*(unsigned int)sizeof(PTRTYPE *),false); - } + PTRTYPE* p = Get(x); + if (delfunc && p) + delfunc(p); + m_hb.Resize(x * (unsigned int)sizeof(PTRTYPE*), false); } - void EmptySafe(bool wantDelete=false,void (*delfunc)(void *)=NULL) + } + void EmptySafe(bool wantDelete = false, void (*delfunc)(void*) = NULL) + { + if (!wantDelete) + Empty(); + else { - if (!wantDelete) Empty(); - else - { - WDL_PtrList tmp; - int x; - for(x=0;x tmp; + int x; + for (x = 0; x < GetSize(); x++) + tmp.Add(Get(x)); + Empty(); + tmp.Empty(true, delfunc); } + } - int LowerBound(const PTRTYPE *key, bool* ismatch, int (*compar)(const PTRTYPE **a, const PTRTYPE **b)) const + int LowerBound(const PTRTYPE* key, bool* ismatch, int (*compar)(const PTRTYPE** a, const PTRTYPE** b)) const + { + int a = 0; + int c = GetSize(); + PTRTYPE** list = GetList(); + while (a != c) { - int a = 0; - int c = GetSize(); - PTRTYPE **list=GetList(); - while (a != c) + int b = (a + c) / 2; + int cmp = compar((const PTRTYPE**)&key, (const PTRTYPE**)(list + b)); + if (cmp > 0) + a = b + 1; + else if (cmp < 0) + c = b; + else { - int b = (a+c)/2; - int cmp = compar((const PTRTYPE **)&key, (const PTRTYPE **)(list+b)); - if (cmp > 0) a = b+1; - else if (cmp < 0) c = b; - else - { - *ismatch = true; - return b; - } + *ismatch = true; + return b; } - *ismatch = false; - return a; } + *ismatch = false; + return a; + } - void Compact() { m_hb.Resize(m_hb.GetSize(),true); } + void Compact() { m_hb.Resize(m_hb.GetSize(), true); } - int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to remove item. returns number removed + int DeleteBatch(bool (*proc)(PTRTYPE* p, void* ctx), + void* ctx = NULL) // proc returns true to remove item. returns number removed + { + const int sz = GetSize(); + int cnt = 0; + PTRTYPE **rd = GetList(), **wr = rd; + for (int x = 0; x < sz; x++) { - const int sz = GetSize(); - int cnt=0; - PTRTYPE **rd = GetList(), **wr = rd; - for (int x = 0; x < sz; x ++) + if (!proc(*rd, ctx)) { - if (!proc(*rd,ctx)) - { - if (rd != wr) *wr=*rd; - wr++; - cnt++; - } - rd++; + if (rd != wr) + *wr = *rd; + wr++; + cnt++; } - if (cnt < sz) m_hb.Resize(cnt * sizeof(PTRTYPE*),false); - return sz - cnt; + rd++; } + if (cnt < sz) + m_hb.Resize(cnt * sizeof(PTRTYPE*), false); + return sz - cnt; + } - private: - WDL_HeapBuf m_hb; - +private: + WDL_HeapBuf m_hb; }; -template class WDL_PtrList_DeleteOnDestroy : public WDL_PtrList +template +class WDL_PtrList_DeleteOnDestroy : public WDL_PtrList { public: - explicit WDL_PtrList_DeleteOnDestroy(void (*delfunc)(void *)=NULL, int defgran=4096) : WDL_PtrList(defgran), m_delfunc(delfunc) { } - ~WDL_PtrList_DeleteOnDestroy() + explicit WDL_PtrList_DeleteOnDestroy(void (*delfunc)(void*) = NULL, int defgran = 4096) + : WDL_PtrList(defgran) + , m_delfunc(delfunc) { - WDL_PtrList::EmptySafe(true,m_delfunc); } + ~WDL_PtrList_DeleteOnDestroy() { WDL_PtrList::EmptySafe(true, m_delfunc); } + private: - void (*m_delfunc)(void *); + void (*m_delfunc)(void*); }; #endif - diff --git a/dsp/ResamplingContainer/Dependencies/WDL/wdltypes.h b/dsp/ResamplingContainer/Dependencies/WDL/wdltypes.h index 7869e7b..1c23f29 100644 --- a/dsp/ResamplingContainer/Dependencies/WDL/wdltypes.h +++ b/dsp/ResamplingContainer/Dependencies/WDL/wdltypes.h @@ -29,18 +29,18 @@ typedef unsigned long long WDL_UINT64; #define WDL_PRI_INT64 "lld" #endif -#if !defined(_MSC_VER) || _MSC_VER > 1200 -#define WDL_DLGRET INT_PTR CALLBACK +#if !defined(_MSC_VER) || _MSC_VER > 1200 + #define WDL_DLGRET INT_PTR CALLBACK #else -#define WDL_DLGRET BOOL CALLBACK + #define WDL_DLGRET BOOL CALLBACK #endif #ifdef _WIN32 -#include -#include + #include + #include #else -#include + #include typedef intptr_t INT_PTR; typedef uintptr_t UINT_PTR; #endif @@ -53,25 +53,25 @@ typedef bool WDL_bool; #endif #ifndef GWLP_USERDATA -#define GWLP_USERDATA GWL_USERDATA -#define GWLP_WNDPROC GWL_WNDPROC -#define GWLP_HINSTANCE GWL_HINSTANCE -#define GWLP_HWNDPARENT GWL_HWNDPARENT -#define DWLP_USER DWL_USER -#define DWLP_DLGPROC DWL_DLGPROC -#define DWLP_MSGRESULT DWL_MSGRESULT -#define SetWindowLongPtr(a,b,c) SetWindowLong(a,b,c) -#define GetWindowLongPtr(a,b) GetWindowLong(a,b) -#define SetWindowLongPtrW(a,b,c) SetWindowLongW(a,b,c) -#define GetWindowLongPtrW(a,b) GetWindowLongW(a,b) -#define SetWindowLongPtrA(a,b,c) SetWindowLongA(a,b,c) -#define GetWindowLongPtrA(a,b) GetWindowLongA(a,b) - -#define GCLP_WNDPROC GCL_WNDPROC -#define GCLP_HICON GCL_HICON -#define GCLP_HICONSM GCL_HICONSM -#define SetClassLongPtr(a,b,c) SetClassLong(a,b,c) -#define GetClassLongPtr(a,b) GetClassLong(a,b) + #define GWLP_USERDATA GWL_USERDATA + #define GWLP_WNDPROC GWL_WNDPROC + #define GWLP_HINSTANCE GWL_HINSTANCE + #define GWLP_HWNDPARENT GWL_HWNDPARENT + #define DWLP_USER DWL_USER + #define DWLP_DLGPROC DWL_DLGPROC + #define DWLP_MSGRESULT DWL_MSGRESULT + #define SetWindowLongPtr(a, b, c) SetWindowLong(a, b, c) + #define GetWindowLongPtr(a, b) GetWindowLong(a, b) + #define SetWindowLongPtrW(a, b, c) SetWindowLongW(a, b, c) + #define GetWindowLongPtrW(a, b) GetWindowLongW(a, b) + #define SetWindowLongPtrA(a, b, c) SetWindowLongA(a, b, c) + #define GetWindowLongPtrA(a, b) GetWindowLongA(a, b) + + #define GCLP_WNDPROC GCL_WNDPROC + #define GCLP_HICON GCL_HICON + #define GCLP_HICONSM GCL_HICONSM + #define SetClassLongPtr(a, b, c) SetClassLong(a, b, c) + #define GetClassLongPtr(a, b) GetClassLong(a, b) #endif #if !defined(WDL_BIG_ENDIAN) && !defined(WDL_LITTLE_ENDIAN) @@ -83,59 +83,62 @@ typedef bool WDL_bool; #endif #if defined(WDL_BIG_ENDIAN) && defined(WDL_LITTLE_ENDIAN) -#error WDL_BIG_ENDIAN and WDL_LITTLE_ENDIAN both defined + #error WDL_BIG_ENDIAN and WDL_LITTLE_ENDIAN both defined #endif #ifdef __GNUC__ -// for structures that contain doubles, or doubles in structures that are after stuff of questionable alignment (for OSX/linux) - #define WDL_FIXALIGN __attribute__ ((aligned (8))) -// usage: void func(int a, const char *fmt, ...) WDL_VARARG_WARN(printf,2,3); // note: if member function, this pointer is counted as well, so as member function that would be 3,4 - #define WDL_VARARG_WARN(x,n,s) __attribute__ ((format (x,n,s))) + // for structures that contain doubles, or doubles in structures that are after stuff of questionable alignment (for + // OSX/linux) + #define WDL_FIXALIGN __attribute__((aligned(8))) + // usage: void func(int a, const char *fmt, ...) WDL_VARARG_WARN(printf,2,3); // note: if member function, this + // pointer is counted as well, so as member function that would be 3,4 + #define WDL_VARARG_WARN(x, n, s) __attribute__((format(x, n, s))) #define WDL_STATICFUNC_UNUSED __attribute__((unused)) #else - #define WDL_FIXALIGN - #define WDL_VARARG_WARN(x,n,s) + #define WDL_FIXALIGN + #define WDL_VARARG_WARN(x, n, s) #define WDL_STATICFUNC_UNUSED #endif #ifndef WDL_WANT_NEW_EXCEPTIONS -#if defined(__cplusplus) -#include -#define WDL_NEW (std::nothrow) -#endif + #if defined(__cplusplus) + #include + #define WDL_NEW (std::nothrow) + #endif #else -#define WDL_NEW + #define WDL_NEW #endif #if !defined(max) && defined(WDL_DEFINE_MINMAX) -#define max(x,y) ((x)<(y)?(y):(x)) -#define min(x,y) ((x)<(y)?(x):(y)) + #define max(x, y) ((x) < (y) ? (y) : (x)) + #define min(x, y) ((x) < (y) ? (x) : (y)) #endif #ifndef wdl_max -#define wdl_max(x,y) ((x)<(y)?(y):(x)) -#define wdl_min(x,y) ((x)<(y)?(x):(y)) -#define wdl_abs(x) ((x)<0 ? -(x) : (x)) -#define wdl_clamp(x,minv,maxv) (WDL_NOT_NORMALLY((maxv) < (minv)) || (x) < (minv) ? (minv) : ((x) > (maxv) ? (maxv) : (x))) + #define wdl_max(x, y) ((x) < (y) ? (y) : (x)) + #define wdl_min(x, y) ((x) < (y) ? (x) : (y)) + #define wdl_abs(x) ((x) < 0 ? -(x) : (x)) + #define wdl_clamp(x, minv, maxv) \ + (WDL_NOT_NORMALLY((maxv) < (minv)) || (x) < (minv) ? (minv) : ((x) > (maxv) ? (maxv) : (x))) #endif #ifndef _WIN32 - #ifndef strnicmp - #define strnicmp(x,y,z) strncasecmp(x,y,z) + #ifndef strnicmp + #define strnicmp(x, y, z) strncasecmp(x, y, z) #endif - #ifndef stricmp - #define stricmp(x,y) strcasecmp(x,y) + #ifndef stricmp + #define stricmp(x, y) strcasecmp(x, y) #endif #endif #ifdef WDL_BACKSLASHES_ARE_ORDINARY -#define WDL_IS_DIRCHAR(x) ((x) == '/') + #define WDL_IS_DIRCHAR(x) ((x) == '/') #else -// for multi-platform applications it seems better to treat backslashes as directory separators even if it -// isn't supported by the underying system (for resolving filenames, etc) + // for multi-platform applications it seems better to treat backslashes as directory separators even if it + // isn't supported by the underying system (for resolving filenames, etc) #ifdef _WIN32 #define WDL_IS_DIRCHAR(x) ((x) == '\\' || (x) == '/') #else @@ -144,46 +147,61 @@ typedef bool WDL_bool; #endif #if defined(_WIN32) && !defined(WDL_BACKSLASHES_ARE_ORDINARY) -#define WDL_DIRCHAR '\\' -#define WDL_DIRCHAR_STR "\\" + #define WDL_DIRCHAR '\\' + #define WDL_DIRCHAR_STR "\\" #else -#define WDL_DIRCHAR '/' -#define WDL_DIRCHAR_STR "/" + #define WDL_DIRCHAR '/' + #define WDL_DIRCHAR_STR "/" #endif #if defined(_WIN32) || defined(__APPLE__) // on __APPLE__ we should ideally check the filesystem for case-sensitivity, assuming a case-insensitive-only match - #define wdl_filename_cmp(x,y) stricmp(x,y) - #define wdl_filename_cmpn(x,y,n) strnicmp(x,y,n) + #define wdl_filename_cmp(x, y) stricmp(x, y) + #define wdl_filename_cmpn(x, y, n) strnicmp(x, y, n) #else - #define wdl_filename_cmp(x,y) strcmp(x,y) - #define wdl_filename_cmpn(x,y,n) strncmp(x,y,n) + #define wdl_filename_cmp(x, y) strcmp(x, y) + #define wdl_filename_cmpn(x, y, n) strncmp(x, y, n) #endif #if defined(__GNUC__) || defined(__INTEL_COMPILER) - #define WDL_likely(x) (__builtin_expect(!!(x),1)) - #define WDL_unlikely(x) (__builtin_expect(!!(x),0)) + #define WDL_likely(x) (__builtin_expect(!!(x), 1)) + #define WDL_unlikely(x) (__builtin_expect(!!(x), 0)) #else #define WDL_likely(x) (!!(x)) #define WDL_unlikely(x) (!!(x)) #endif #if defined(_DEBUG) || defined(DEBUG) -#include + #include #ifdef _MSC_VER - // msvc assert failure allows message loop to run, potentially resulting in recursive asserts - static LONG WDL_ASSERT_INTERNALCNT; - static int WDL_ASSERT_END() { WDL_ASSERT_INTERNALCNT=0; return 0; } - static int WDL_ASSERT_BEGIN() { return InterlockedCompareExchange(&WDL_ASSERT_INTERNALCNT,1,0) == 0; } - #define WDL_ASSERT(x) do { if (WDL_ASSERT_BEGIN()) { assert(x); WDL_ASSERT_END(); } } while(0) +// msvc assert failure allows message loop to run, potentially resulting in recursive asserts +static LONG WDL_ASSERT_INTERNALCNT; +static int WDL_ASSERT_END() +{ + WDL_ASSERT_INTERNALCNT = 0; + return 0; +} +static int WDL_ASSERT_BEGIN() +{ + return InterlockedCompareExchange(&WDL_ASSERT_INTERNALCNT, 1, 0) == 0; +} + #define WDL_ASSERT(x) \ + do \ + { \ + if (WDL_ASSERT_BEGIN()) \ + { \ + assert(x); \ + WDL_ASSERT_END(); \ + } \ + } while (0) #else #define WDL_ASSERT_BEGIN() (1) #define WDL_ASSERT_END() (0) #define WDL_ASSERT(x) assert(x) #endif - #define WDL_NORMALLY(x) ((x) ? 1 : (WDL_ASSERT_BEGIN() && (assert(0/*ignorethis*/ && (x)),WDL_ASSERT_END()))) - #define WDL_NOT_NORMALLY(x) ((x) ? !WDL_ASSERT_BEGIN() || (assert(0/*ignorethis*/ && !(x)),!WDL_ASSERT_END()) : 0) + #define WDL_NORMALLY(x) ((x) ? 1 : (WDL_ASSERT_BEGIN() && (assert(0 /*ignorethis*/ && (x)), WDL_ASSERT_END()))) + #define WDL_NOT_NORMALLY(x) ((x) ? !WDL_ASSERT_BEGIN() || (assert(0 /*ignorethis*/ && !(x)), !WDL_ASSERT_END()) : 0) #else #define WDL_ASSERT(x) #define WDL_NORMALLY(x) WDL_likely(x) @@ -193,18 +211,20 @@ typedef bool WDL_bool; typedef unsigned int WDL_TICKTYPE; -static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE(WDL_TICKTYPE current, WDL_TICKTYPE refstart, int len) // current >= refstart && current < refstart+len +static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE(WDL_TICKTYPE current, WDL_TICKTYPE refstart, + int len) // current >= refstart && current < refstart+len { WDL_ASSERT(len > 0); return (current - refstart) < (WDL_TICKTYPE)len; } -static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE_ENDING_AT(WDL_TICKTYPE current, WDL_TICKTYPE refend, int len) // current >= refend-len && current < refend +static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE_ENDING_AT(WDL_TICKTYPE current, WDL_TICKTYPE refend, + int len) // current >= refend-len && current < refend { const WDL_TICKTYPE refstart = refend - len; WDL_ASSERT(len > 0); return (current - refstart) < (WDL_TICKTYPE)len; - //return ((refend-1) - current) < (WDL_TICKTYPE)len; + // return ((refend-1) - current) < (WDL_TICKTYPE)len; } // use this if you want validate that nothing that includes wdltypes.h calls fopen() directly on win32 @@ -215,12 +235,15 @@ static WDL_bool WDL_STATICFUNC_UNUSED WDL_TICKS_IN_RANGE_ENDING_AT(WDL_TICKTYPE #undef fopen #endif #include - static WDL_STATICFUNC_UNUSED FILE *WDL_fopenA(const char *fn, const char *mode) { return fopen(fn,mode); } +static WDL_STATICFUNC_UNUSED FILE* WDL_fopenA(const char* fn, const char* mode) +{ + return fopen(fn, mode); +} #define fopen this_should_be_fopenUTF8_include_win32_utf8.h #else // callers of WDL_fopenA don't mind being non-UTF8-compatible on win32 // (this could map to either fopen() or fopenUTF8() - #define WDL_fopenA(fn,mode) fopen(fn,mode) + #define WDL_fopenA(fn, mode) fopen(fn, mode) #endif #ifndef WDL_ALLOW_UNSIGNED_DEFAULT_CHAR @@ -229,99 +252,105 @@ typedef char wdl_assert_failed_unsigned_char[((char)-1) > 0 ? -1 : 1]; // wdl_log() / printf() wrapper. no-op on release builds #if !defined(_DEBUG) && !defined(WDL_LOG_ON_RELEASE) - static void WDL_STATICFUNC_UNUSED WDL_VARARG_WARN(printf,1,2) wdl_log(const char *format, ...) { } +static void WDL_STATICFUNC_UNUSED WDL_VARARG_WARN(printf, 1, 2) wdl_log(const char* format, ...) {} #elif defined(_WIN32) - static void WDL_STATICFUNC_UNUSED WDL_VARARG_WARN(printf,1,2) wdl_log(const char *format, ...) - { - int rv; - va_list va; - - char tmp[3800]; - va_start(va,format); - tmp[0]=0; - rv=_vsnprintf(tmp,sizeof(tmp),format,va); // returns -1 if over, and does not null terminate, ugh - va_end(va); - - if (rv < 0 || rv>=(int)sizeof(tmp)-1) tmp[sizeof(tmp)-1]=0; - OutputDebugStringA(tmp); - } +static void WDL_STATICFUNC_UNUSED WDL_VARARG_WARN(printf, 1, 2) wdl_log(const char* format, ...) +{ + int rv; + va_list va; + + char tmp[3800]; + va_start(va, format); + tmp[0] = 0; + rv = _vsnprintf(tmp, sizeof(tmp), format, va); // returns -1 if over, and does not null terminate, ugh + va_end(va); + + if (rv < 0 || rv >= (int)sizeof(tmp) - 1) + tmp[sizeof(tmp) - 1] = 0; + OutputDebugStringA(tmp); +} #else #define wdl_log printf #endif -static void WDL_STATICFUNC_UNUSED wdl_bswap_copy(void *bout, const void *bin, size_t nelem, size_t elemsz) +static void WDL_STATICFUNC_UNUSED wdl_bswap_copy(void* bout, const void* bin, size_t nelem, size_t elemsz) { char p[8], po[8]; WDL_ASSERT(elemsz > 0); if (elemsz > 1 && WDL_NORMALLY(elemsz <= sizeof(p))) { - size_t i,x; - for (i = 0; i < nelem; i ++) + size_t i, x; + for (i = 0; i < nelem; i++) { - memcpy(p,bin,elemsz); - for (x = 0; x < elemsz; x ++) po[x]=p[elemsz-1-x]; - memcpy(bout,po,elemsz); - bin = (const char *)bin + elemsz; - bout = (char *)bout + elemsz; + memcpy(p, bin, elemsz); + for (x = 0; x < elemsz; x++) + po[x] = p[elemsz - 1 - x]; + memcpy(bout, po, elemsz); + bin = (const char*)bin + elemsz; + bout = (char*)bout + elemsz; } } else if (bout != bin) - memmove(bout,bin,elemsz * nelem); + memmove(bout, bin, elemsz * nelem); } -static void WDL_STATICFUNC_UNUSED wdl_memcpy_le(void *bout, const void *bin, size_t nelem, size_t elemsz) +static void WDL_STATICFUNC_UNUSED wdl_memcpy_le(void* bout, const void* bin, size_t nelem, size_t elemsz) { WDL_ASSERT(elemsz > 0 && elemsz <= 8); #ifdef WDL_BIG_ENDIAN - if (elemsz > 1) wdl_bswap_copy(bout,bin,nelem,elemsz); + if (elemsz > 1) + wdl_bswap_copy(bout, bin, nelem, elemsz); else #endif - if (bout != bin) memmove(bout,bin,elemsz * nelem); + if (bout != bin) + memmove(bout, bin, elemsz * nelem); } -static void WDL_STATICFUNC_UNUSED wdl_memcpy_be(void *bout, const void *bin, size_t nelem, size_t elemsz) +static void WDL_STATICFUNC_UNUSED wdl_memcpy_be(void* bout, const void* bin, size_t nelem, size_t elemsz) { WDL_ASSERT(elemsz > 0 && elemsz <= 8); #ifdef WDL_LITTLE_ENDIAN - if (elemsz > 1) wdl_bswap_copy(bout,bin,nelem,elemsz); + if (elemsz > 1) + wdl_bswap_copy(bout, bin, nelem, elemsz); else #endif - if (bout != bin) memmove(bout,bin,elemsz * nelem); + if (bout != bin) + memmove(bout, bin, elemsz * nelem); } -static void WDL_STATICFUNC_UNUSED wdl_mem_store_int(void *bout, int v) +static void WDL_STATICFUNC_UNUSED wdl_mem_store_int(void* bout, int v) { - memcpy(bout,&v,sizeof(v)); + memcpy(bout, &v, sizeof(v)); } -static void WDL_STATICFUNC_UNUSED wdl_mem_store_int_le(void *bout, int v) +static void WDL_STATICFUNC_UNUSED wdl_mem_store_int_le(void* bout, int v) { - wdl_memcpy_le(bout,&v,1,sizeof(v)); + wdl_memcpy_le(bout, &v, 1, sizeof(v)); } -static void WDL_STATICFUNC_UNUSED wdl_mem_store_int_be(void *bout, int v) +static void WDL_STATICFUNC_UNUSED wdl_mem_store_int_be(void* bout, int v) { - wdl_memcpy_be(bout,&v,1,sizeof(v)); + wdl_memcpy_be(bout, &v, 1, sizeof(v)); } -static int WDL_STATICFUNC_UNUSED wdl_mem_load_int(const void *rd) +static int WDL_STATICFUNC_UNUSED wdl_mem_load_int(const void* rd) { int v; - memcpy(&v,rd,sizeof(v)); + memcpy(&v, rd, sizeof(v)); return v; } -static int WDL_STATICFUNC_UNUSED wdl_mem_load_int_le(const void *rd) +static int WDL_STATICFUNC_UNUSED wdl_mem_load_int_le(const void* rd) { int v; - wdl_memcpy_le(&v,rd,1,sizeof(v)); + wdl_memcpy_le(&v, rd, 1, sizeof(v)); return v; } -static int WDL_STATICFUNC_UNUSED wdl_mem_load_int_be(const void *rd) +static int WDL_STATICFUNC_UNUSED wdl_mem_load_int_be(const void* rd) { int v; - wdl_memcpy_be(&v,rd,1,sizeof(v)); + wdl_memcpy_be(&v, rd, 1, sizeof(v)); return v; } diff --git a/dsp/ResamplingContainer/ResamplingContainer.h b/dsp/ResamplingContainer/ResamplingContainer.h index eeecd47..4b8eb6c 100644 --- a/dsp/ResamplingContainer/ResamplingContainer.h +++ b/dsp/ResamplingContainer/ResamplingContainer.h @@ -10,7 +10,7 @@ // ------------------------------------------------------------------------------------- /* -iPlug 2 C++ Plug-in Framework. +iPlug 2 C++ Plug-in Framework. Copyright (C) the iPlug 2 Developers. Portions copyright other contributors, see each source file for more information. @@ -18,19 +18,24 @@ Based on WDL-OL/iPlug by Oli Larkin (2011-2018), and the original iPlug v1 (2008 LICENSE: -This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable +for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it +and redistribute it freely, subject to the following restrictions: -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If +you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not +required. +1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original +software. 1. This notice may not be removed or altered from any source distribution. iPlug 2 includes the following 3rd party libraries (see each license info): * Cockos WDL https://www.cockos.com/wdl * NanoVG https://github.com/memononen/nanovg -* NanoSVG https://github.com/memononen/nanosvg +* NanoSVG https://github.com/memononen/nanosvg * MetalNanoVG https://github.com/ollix/MetalNanoVG * RTAudio https://www.music.mcgill.ca/~gary/rtaudio * RTMidi https://www.music.mcgill.ca/~gary/rtmidi @@ -50,7 +55,8 @@ iPlug 2 includes the following 3rd party libraries (see each license info): #include "Dependencies/LanczosResampler.h" -namespace dsp { +namespace dsp +{ /** A multi-channel real-time resampling container that can be used to resample * audio processing to a specified sample rate for the situation where you have @@ -62,8 +68,8 @@ namespace dsp { * - Cubic interpolation: cubic interpolation between samples * - Lanczos: Lanczos resampling uses an approximation of the sinc function to * interpolate between samples. This is the highest quality resampling mode. - * - * The Lanczos resampler has a configurable filter size (A) that affects the + * + * The Lanczos resampler has a configurable filter size (A) that affects the * latency of the resampler. It can also optionally use SIMD instructions to * when T==float. * @@ -71,11 +77,11 @@ namespace dsp { * @tparam T the sampletype * @tparam NCHANS the number of channels * @tparam A The Lanczos filter size for the LanczosResampler resampler mode - A higher value makes the filter closer to an - ideal stop-band that rejects high-frequency content (anti-aliasing), + A higher value makes the filter closer to an + ideal stop-band that rejects high-frequency content (anti-aliasing), but at the expense of higher latency */ -template +template class ResamplingContainer { public: @@ -87,10 +93,10 @@ class ResamplingContainer : mRenderingSampleRate(renderingSampleRate) { } - + ResamplingContainer(const ResamplingContainer&) = delete; ResamplingContainer& operator=(const ResamplingContainer&) = delete; - + // :param inputSampleRate: The external sample rate interacting with this object. // :param blockSize: The largest block size that will be given to this class to process until Reset() is called // again. @@ -108,15 +114,15 @@ class ResamplingContainer // The buffers for the encapsulated code need to be long enough to hold the correesponding number of samples mMaxBlockSize = blockSize; mMaxEncapsulatedBlockSize = MaxEncapsulatedBlockSize(blockSize); - - mScratchExternalInputData.Resize(mMaxBlockSize * NCHANS); // This may contain junk right now. - mEncapsulatedInputData.Resize(mMaxEncapsulatedBlockSize * NCHANS); // This may contain junk right now. - mEncapsulatedOutputData.Resize(mMaxEncapsulatedBlockSize * NCHANS); // This may contain junk right now. + + mScratchExternalInputData.Resize(mMaxBlockSize * NCHANS); // This may contain junk right now. + mEncapsulatedInputData.Resize(mMaxEncapsulatedBlockSize * NCHANS); // This may contain junk right now. + mEncapsulatedOutputData.Resize(mMaxEncapsulatedBlockSize * NCHANS); // This may contain junk right now. mScratchExternalInputPointers.Empty(); mEncapsulatedInputPointers.Empty(); mEncapsulatedOutputPointers.Empty(); - - for (auto chan=0; chan(mInputSampleRate, mRenderingSampleRate); mResampler2 = std::make_unique(mRenderingSampleRate, mInputSampleRate); - + // Zeroes the scratch pointers so that we warm up with silence. ClearBuffers(); - + // Warm up the resampling container with enough silence that the first real buffer can yield the required number // of output samples. const auto midSamples = mResampler2->GetNumSamplesRequiredFor(1); @@ -138,7 +144,8 @@ class ResamplingContainer // mResampler1->PushBlock(mScratchExternalInputPointers.GetList(), mLatency); const size_t populated = mResampler1->PopBlock(mEncapsulatedInputPointers.GetList(), midSamples); - if (populated < midSamples) { + if (populated < midSamples) + { throw std::runtime_error("Didn't get enough samples required for pre-population!"); } // 2. "process" the warm-up in the encapsulated DSP. @@ -156,46 +163,48 @@ class ResamplingContainer * @param inputs Two-dimensional array containing the non-interleaved input buffers of audio samples for all channels * @param outputs Two-dimensional array for audio output (non-interleaved). * @param nFrames The block size for this block: number of samples per channel. - * @param func The function that processes the audio sample at the higher sampling rate. NOTE: std::function can call malloc if you pass in captures */ + * @param func The function that processes the audio sample at the higher sampling rate. NOTE: std::function can call + * malloc if you pass in captures */ void ProcessBlock(T** inputs, T** outputs, int nFrames, BlockProcessFunc func) { - mResampler1->PushBlock(inputs, nFrames); - // This is the most samples the encapsualted context might get. Sometimes it'll get fewer. - const auto maxEncapsulatedLen = MaxEncapsulatedBlockSize(nFrames); - - // Process as much audio as you can with the encapsulated DSP, and push it into the second resampler. - // This will give the second reasmpler enough for it to pop the required buffer size to complete this function - // correctly. - while (mResampler1->GetNumSamplesRequiredFor(1) == 0) // i.e. there's more to process - { - // Get a block no larger than the encapsulated DSP is expecting. - const size_t populated1 = mResampler1->PopBlock(mEncapsulatedInputPointers.GetList(), maxEncapsulatedLen); - if (populated1 > maxEncapsulatedLen) { - throw std::runtime_error("Got more encapsulated samples than the encapsulated DSP is prepared to handle!"); - } - func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), (int)populated1); - // And push the results into the second resampler so that it has what the external context requires. - mResampler2->PushBlock(mEncapsulatedOutputPointers.GetList(), populated1); - } - - // Pop the required output from the second resampler for the external context. - const auto populated2 = mResampler2->PopBlock(outputs, nFrames); - if (populated2 < nFrames) { - throw std::runtime_error("Did not yield enough samples to provide the required output buffer!"); - } - // Get ready for the next block: - mResampler1->RenormalizePhases(); - mResampler2->RenormalizePhases(); + mResampler1->PushBlock(inputs, nFrames); + // This is the most samples the encapsualted context might get. Sometimes it'll get fewer. + const auto maxEncapsulatedLen = MaxEncapsulatedBlockSize(nFrames); + + // Process as much audio as you can with the encapsulated DSP, and push it into the second resampler. + // This will give the second reasmpler enough for it to pop the required buffer size to complete this function + // correctly. + while (mResampler1->GetNumSamplesRequiredFor(1) == 0) // i.e. there's more to process + { + // Get a block no larger than the encapsulated DSP is expecting. + const size_t populated1 = mResampler1->PopBlock(mEncapsulatedInputPointers.GetList(), maxEncapsulatedLen); + if (populated1 > maxEncapsulatedLen) + { + throw std::runtime_error("Got more encapsulated samples than the encapsulated DSP is prepared to handle!"); + } + func(mEncapsulatedInputPointers.GetList(), mEncapsulatedOutputPointers.GetList(), (int)populated1); + // And push the results into the second resampler so that it has what the external context requires. + mResampler2->PushBlock(mEncapsulatedOutputPointers.GetList(), populated1); + } + + // Pop the required output from the second resampler for the external context. + const auto populated2 = mResampler2->PopBlock(outputs, nFrames); + if (populated2 < nFrames) + { + throw std::runtime_error("Did not yield enough samples to provide the required output buffer!"); + } + // Get ready for the next block: + mResampler1->RenormalizePhases(); + mResampler2->RenormalizePhases(); } - + int GetLatency() const { return mLatency; } private: static inline int LinearInterpolate(T** inputs, T** outputs, int inputLen, double ratio, int maxOutputLen) { // FIXME check through this! - const auto outputLen = - std::min(static_cast(std::ceil(static_cast(inputLen) / ratio)), maxOutputLen); + const auto outputLen = std::min(static_cast(std::ceil(static_cast(inputLen) / ratio)), maxOutputLen); for (auto writePos = 0; writePos < outputLen; writePos++) { @@ -207,10 +216,10 @@ class ResamplingContainer { const auto y = readPos - readPostionTrunc; - for (auto chan=0; chan(std::ceil(static_cast(inputLen) / ratio)), maxOutputLen); + const auto outputLen = std::min(static_cast(std::ceil(static_cast(inputLen) / ratio)), maxOutputLen); for (auto writePos = 0; writePos < outputLen; writePos++) { @@ -234,53 +242,58 @@ class ResamplingContainer if (readPosInt < inputLen) { const auto y = readPos - readPostionTrunc; - - for (auto chan=0; chan 0) ? inputs[chan][readPosInt - 1] : 0.0f; - const auto x0 = ((readPosInt) < inputLen) ? inputs[chan][readPosInt] : inputs[chan][readPosInt-1]; - const auto x1 = ((readPosInt + 1) < inputLen) ? inputs[chan][readPosInt + 1] : inputs[chan][readPosInt-1]; - const auto x2 = ((readPosInt + 2) < inputLen) ? inputs[chan][readPosInt + 2] : inputs[chan][readPosInt-1]; - - const auto c = (x1 - xm1) * 0.5; - const auto v = x0 - x1; - const auto w = c + v; - const auto a = w + v + (x2 - x0) * 0.5; - const auto b = w + a; - - outputs[chan][writePos] = ((((a * y) -b) * y + c) * y + x0); + const auto x0 = ((readPosInt) < inputLen) ? inputs[chan][readPosInt] : inputs[chan][readPosInt - 1]; + const auto x1 = ((readPosInt + 1) < inputLen) ? inputs[chan][readPosInt + 1] : inputs[chan][readPosInt - 1]; + const auto x2 = ((readPosInt + 2) < inputLen) ? inputs[chan][readPosInt + 2] : inputs[chan][readPosInt - 1]; + + const auto c = (x1 - xm1) * 0.5; + const auto v = x0 - x1; + const auto w = c + v; + const auto a = w + v + (x2 - x0) * 0.5; + const auto b = w + a; + + outputs[chan][writePos] = ((((a * y) - b) * y + c) * y + x0); } } } - + return outputLen; } - + void ClearBuffers() { memset(mScratchExternalInputData.Get(), 0.0f, DataSize(mMaxBlockSize)); const auto encapsulatedDataSize = DataSize(mMaxEncapsulatedBlockSize); memset(mEncapsulatedInputData.Get(), 0.0f, encapsulatedDataSize); memset(mEncapsulatedOutputData.Get(), 0.0f, encapsulatedDataSize); - - if (mResampler1 != nullptr) { - mResampler1->ClearBuffer(); - } - if (mResampler2 != nullptr) { - mResampler2->ClearBuffer(); - } + + if (mResampler1 != nullptr) + { + mResampler1->ClearBuffer(); + } + if (mResampler2 != nullptr) + { + mResampler2->ClearBuffer(); + } } - + // How big could the corresponding encapsulated buffer be for a buffer at the external sample rate of a given size? - int MaxEncapsulatedBlockSize(const int externalBlockSize) const { + int MaxEncapsulatedBlockSize(const int externalBlockSize) const + { return static_cast(std::ceil(static_cast(externalBlockSize) / mRatio1)); } - + // Size of the multi-channel data for a given block size - size_t DataSize(const int blockSize) const { return blockSize * NCHANS * sizeof(T);}; - - void FallbackFunc(T** inputs, T** outputs, int n) { - for (int i=0; i Date: Mon, 1 Jan 2024 17:50:46 -0800 Subject: [PATCH 7/7] Fix build requires std=c++20 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e7db1e..1942a22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED OFF) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++20") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") include_directories(SYSTEM /usr/local/include)