From 25b8a4b4893689c63d45eacf8ac3428a972d2d82 Mon Sep 17 00:00:00 2001 From: dreamer Date: Fri, 16 Sep 2022 15:34:36 +0200 Subject: [PATCH] test --- Makefile | 47 ++ README.md | 10 + delay_simple/README.md | 27 + delay_simple/c/HeavyContext.cpp | 264 +++++++ delay_simple/c/HeavyContext.hpp | 107 +++ delay_simple/c/HeavyContextInterface.hpp | 291 +++++++ delay_simple/c/Heavy_delay_simple.cpp | 659 ++++++++++++++++ delay_simple/c/Heavy_delay_simple.h | 85 ++ delay_simple/c/Heavy_delay_simple.hpp | 162 ++++ delay_simple/c/HvControlBinop.c | 100 +++ delay_simple/c/HvControlBinop.h | 71 ++ delay_simple/c/HvControlCast.c | 60 ++ delay_simple/c/HvControlCast.h | 39 + delay_simple/c/HvControlDelay.c | 92 +++ delay_simple/c/HvControlDelay.h | 44 ++ delay_simple/c/HvControlSystem.c | 46 ++ delay_simple/c/HvControlSystem.h | 33 + delay_simple/c/HvControlTabhead.c | 45 ++ delay_simple/c/HvControlTabhead.h | 39 + delay_simple/c/HvControlVar.c | 83 ++ delay_simple/c/HvControlVar.h | 43 ++ delay_simple/c/HvHeavy.cpp | 318 ++++++++ delay_simple/c/HvHeavy.h | 413 ++++++++++ delay_simple/c/HvHeavyInternal.h | 51 ++ delay_simple/c/HvLightPipe.c | 136 ++++ delay_simple/c/HvLightPipe.h | 104 +++ delay_simple/c/HvMath.h | 724 ++++++++++++++++++ delay_simple/c/HvMessage.c | 199 +++++ delay_simple/c/HvMessage.h | 183 +++++ delay_simple/c/HvMessagePool.c | 144 ++++ delay_simple/c/HvMessagePool.h | 67 ++ delay_simple/c/HvMessageQueue.c | 215 ++++++ delay_simple/c/HvMessageQueue.h | 101 +++ delay_simple/c/HvSignalTabread.c | 77 ++ delay_simple/c/HvSignalTabread.h | 183 +++++ delay_simple/c/HvSignalTabwrite.c | 54 ++ delay_simple/c/HvSignalTabwrite.h | 147 ++++ delay_simple/c/HvSignalVar.c | 75 ++ delay_simple/c/HvSignalVar.h | 94 +++ delay_simple/c/HvTable.c | 110 +++ delay_simple/c/HvTable.h | 88 +++ delay_simple/c/HvUtils.c | 54 ++ delay_simple/c/HvUtils.h | 317 ++++++++ delay_simple/hv/dpf_delay_simple.hv.json | 1 + delay_simple/ir/delay_simple.heavy.ir.json | 1 + delay_simple/plugin/README.md | 27 + delay_simple/plugin/build.json | 1 + .../plugin/source/DistrhoPluginInfo.h | 52 ++ delay_simple/plugin/source/HeavyContext.cpp | 264 +++++++ delay_simple/plugin/source/HeavyContext.hpp | 107 +++ .../plugin/source/HeavyContextInterface.hpp | 291 +++++++ .../plugin/source/HeavyDPF_delay_simple.cpp | 568 ++++++++++++++ .../plugin/source/HeavyDPF_delay_simple.hpp | 167 ++++ .../plugin/source/Heavy_delay_simple.cpp | 659 ++++++++++++++++ .../plugin/source/Heavy_delay_simple.h | 85 ++ .../plugin/source/Heavy_delay_simple.hpp | 162 ++++ delay_simple/plugin/source/HvControlBinop.c | 100 +++ delay_simple/plugin/source/HvControlBinop.h | 71 ++ delay_simple/plugin/source/HvControlCast.c | 60 ++ delay_simple/plugin/source/HvControlCast.h | 39 + delay_simple/plugin/source/HvControlDelay.c | 92 +++ delay_simple/plugin/source/HvControlDelay.h | 44 ++ delay_simple/plugin/source/HvControlSystem.c | 46 ++ delay_simple/plugin/source/HvControlSystem.h | 33 + delay_simple/plugin/source/HvControlTabhead.c | 45 ++ delay_simple/plugin/source/HvControlTabhead.h | 39 + delay_simple/plugin/source/HvControlVar.c | 83 ++ delay_simple/plugin/source/HvControlVar.h | 43 ++ delay_simple/plugin/source/HvHeavy.cpp | 318 ++++++++ delay_simple/plugin/source/HvHeavy.h | 413 ++++++++++ delay_simple/plugin/source/HvHeavyInternal.h | 51 ++ delay_simple/plugin/source/HvLightPipe.c | 136 ++++ delay_simple/plugin/source/HvLightPipe.h | 104 +++ delay_simple/plugin/source/HvMath.h | 724 ++++++++++++++++++ delay_simple/plugin/source/HvMessage.c | 199 +++++ delay_simple/plugin/source/HvMessage.h | 183 +++++ delay_simple/plugin/source/HvMessagePool.c | 144 ++++ delay_simple/plugin/source/HvMessagePool.h | 67 ++ delay_simple/plugin/source/HvMessageQueue.c | 215 ++++++ delay_simple/plugin/source/HvMessageQueue.h | 101 +++ delay_simple/plugin/source/HvSignalTabread.c | 77 ++ delay_simple/plugin/source/HvSignalTabread.h | 183 +++++ delay_simple/plugin/source/HvSignalTabwrite.c | 54 ++ delay_simple/plugin/source/HvSignalTabwrite.h | 147 ++++ delay_simple/plugin/source/HvSignalVar.c | 75 ++ delay_simple/plugin/source/HvSignalVar.h | 94 +++ delay_simple/plugin/source/HvTable.c | 110 +++ delay_simple/plugin/source/HvTable.h | 88 +++ delay_simple/plugin/source/HvUtils.c | 54 ++ delay_simple/plugin/source/HvUtils.h | 317 ++++++++ delay_simple/plugin/source/Makefile | 24 + dpf | 1 + dpf_delay_simple.json | 17 + dpf_delay_simple.pd | 39 + 94 files changed, 13216 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 delay_simple/README.md create mode 100644 delay_simple/c/HeavyContext.cpp create mode 100644 delay_simple/c/HeavyContext.hpp create mode 100644 delay_simple/c/HeavyContextInterface.hpp create mode 100644 delay_simple/c/Heavy_delay_simple.cpp create mode 100644 delay_simple/c/Heavy_delay_simple.h create mode 100644 delay_simple/c/Heavy_delay_simple.hpp create mode 100644 delay_simple/c/HvControlBinop.c create mode 100644 delay_simple/c/HvControlBinop.h create mode 100644 delay_simple/c/HvControlCast.c create mode 100644 delay_simple/c/HvControlCast.h create mode 100644 delay_simple/c/HvControlDelay.c create mode 100644 delay_simple/c/HvControlDelay.h create mode 100644 delay_simple/c/HvControlSystem.c create mode 100644 delay_simple/c/HvControlSystem.h create mode 100644 delay_simple/c/HvControlTabhead.c create mode 100644 delay_simple/c/HvControlTabhead.h create mode 100644 delay_simple/c/HvControlVar.c create mode 100644 delay_simple/c/HvControlVar.h create mode 100644 delay_simple/c/HvHeavy.cpp create mode 100644 delay_simple/c/HvHeavy.h create mode 100644 delay_simple/c/HvHeavyInternal.h create mode 100644 delay_simple/c/HvLightPipe.c create mode 100644 delay_simple/c/HvLightPipe.h create mode 100644 delay_simple/c/HvMath.h create mode 100644 delay_simple/c/HvMessage.c create mode 100644 delay_simple/c/HvMessage.h create mode 100644 delay_simple/c/HvMessagePool.c create mode 100644 delay_simple/c/HvMessagePool.h create mode 100644 delay_simple/c/HvMessageQueue.c create mode 100644 delay_simple/c/HvMessageQueue.h create mode 100644 delay_simple/c/HvSignalTabread.c create mode 100644 delay_simple/c/HvSignalTabread.h create mode 100644 delay_simple/c/HvSignalTabwrite.c create mode 100644 delay_simple/c/HvSignalTabwrite.h create mode 100644 delay_simple/c/HvSignalVar.c create mode 100644 delay_simple/c/HvSignalVar.h create mode 100644 delay_simple/c/HvTable.c create mode 100644 delay_simple/c/HvTable.h create mode 100644 delay_simple/c/HvUtils.c create mode 100644 delay_simple/c/HvUtils.h create mode 100644 delay_simple/hv/dpf_delay_simple.hv.json create mode 100644 delay_simple/ir/delay_simple.heavy.ir.json create mode 100644 delay_simple/plugin/README.md create mode 100644 delay_simple/plugin/build.json create mode 100644 delay_simple/plugin/source/DistrhoPluginInfo.h create mode 100644 delay_simple/plugin/source/HeavyContext.cpp create mode 100644 delay_simple/plugin/source/HeavyContext.hpp create mode 100644 delay_simple/plugin/source/HeavyContextInterface.hpp create mode 100644 delay_simple/plugin/source/HeavyDPF_delay_simple.cpp create mode 100644 delay_simple/plugin/source/HeavyDPF_delay_simple.hpp create mode 100644 delay_simple/plugin/source/Heavy_delay_simple.cpp create mode 100644 delay_simple/plugin/source/Heavy_delay_simple.h create mode 100644 delay_simple/plugin/source/Heavy_delay_simple.hpp create mode 100644 delay_simple/plugin/source/HvControlBinop.c create mode 100644 delay_simple/plugin/source/HvControlBinop.h create mode 100644 delay_simple/plugin/source/HvControlCast.c create mode 100644 delay_simple/plugin/source/HvControlCast.h create mode 100644 delay_simple/plugin/source/HvControlDelay.c create mode 100644 delay_simple/plugin/source/HvControlDelay.h create mode 100644 delay_simple/plugin/source/HvControlSystem.c create mode 100644 delay_simple/plugin/source/HvControlSystem.h create mode 100644 delay_simple/plugin/source/HvControlTabhead.c create mode 100644 delay_simple/plugin/source/HvControlTabhead.h create mode 100644 delay_simple/plugin/source/HvControlVar.c create mode 100644 delay_simple/plugin/source/HvControlVar.h create mode 100644 delay_simple/plugin/source/HvHeavy.cpp create mode 100644 delay_simple/plugin/source/HvHeavy.h create mode 100644 delay_simple/plugin/source/HvHeavyInternal.h create mode 100644 delay_simple/plugin/source/HvLightPipe.c create mode 100644 delay_simple/plugin/source/HvLightPipe.h create mode 100644 delay_simple/plugin/source/HvMath.h create mode 100644 delay_simple/plugin/source/HvMessage.c create mode 100644 delay_simple/plugin/source/HvMessage.h create mode 100644 delay_simple/plugin/source/HvMessagePool.c create mode 100644 delay_simple/plugin/source/HvMessagePool.h create mode 100644 delay_simple/plugin/source/HvMessageQueue.c create mode 100644 delay_simple/plugin/source/HvMessageQueue.h create mode 100644 delay_simple/plugin/source/HvSignalTabread.c create mode 100644 delay_simple/plugin/source/HvSignalTabread.h create mode 100644 delay_simple/plugin/source/HvSignalTabwrite.c create mode 100644 delay_simple/plugin/source/HvSignalTabwrite.h create mode 100644 delay_simple/plugin/source/HvSignalVar.c create mode 100644 delay_simple/plugin/source/HvSignalVar.h create mode 100644 delay_simple/plugin/source/HvTable.c create mode 100644 delay_simple/plugin/source/HvTable.h create mode 100644 delay_simple/plugin/source/HvUtils.c create mode 100644 delay_simple/plugin/source/HvUtils.h create mode 100644 delay_simple/plugin/source/Makefile create mode 160000 dpf create mode 100644 dpf_delay_simple.json create mode 100644 dpf_delay_simple.pd diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..288fafd --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +#!/usr/bin/make -f +# Makefile for DISTRHO Plugins # +# ---------------------------- # +# Created by falkTX +# +# Modified by Wasted Audio +# + +DPFER_PATH = dpf + +include ${DPFER_PATH}/Makefile.base.mk + +all: libs plugin gen + +define MISSING_SUBMODULES_ERROR + +Cannot find DGL! Please run "make submodules" to clone the missing submodules, then retry building the plugin. + +endef + +# -------------------------------------------------------------- +submodules: + git submodule update --init --recursive + +libs: + +plugin: libs + $(MAKE) all -C delay_simple/plugin/source + +gen: plugins ${DPFER_PATH}/utils/lv2_ttl_generator + @$(CURDIR)/${DPFER_PATH}/utils/generate-ttl.sh +ifeq ($(MACOS),true) + @$(CURDIR)/${DPFER_PATH}/utils/generate-vst-bundles.sh +endif + +dpf/utils/lv2_ttl_generator: + $(MAKE) -C ${DPFER_PATH}/utils/lv2-ttl-generator + +# -------------------------------------------------------------- + +clean: + $(MAKE) clean -C ${DPFER_PATH}/utils/lv2-ttl-generator + $(MAKE) clean -C plugin/source + +# -------------------------------------------------------------- + +.PHONY: plugins diff --git a/README.md b/README.md new file mode 100644 index 0000000..af0255f --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Heavy DPF examples +--- +This repository contains example Heavy wrappers for the Distrho Plugin Format. + +## Audio Effects + +* Delay: Simple +* Delay: Advanced #todo +* Distortion: Simple #todo +* Distortion: Advanced #todo diff --git a/delay_simple/README.md b/delay_simple/README.md new file mode 100644 index 0000000..95dad59 --- /dev/null +++ b/delay_simple/README.md @@ -0,0 +1,27 @@ +# Distrho Plugin Format + +This output is for the Distrho Plugin Format ([DPF](https://github.com/DISTRHO/DPF)), and can be used to build LV2, VST2 and jack standalone versions of your Heavy code. + +# Build Instructions + +Make sure you have a (recent) DPF in the root of your output directory + +```bash +$ cd +$ git clone https://github.com/DISTRHO/DPF.git dpf +``` + +Then compile the plugins from the source folder: + +```bash +$ make +``` + +This will result in an `bin/` folder with all binary assets. + +* LV2 - move `bin/.lv2/` folder to your local `~/.lv2/` dir +* VST2 - move `bin/-vst.so`, can be placed directly into your `~/.vst/` dir + +## Jack + +The Jack binary can be executed in place and used to test functionality `./bin/`. Currently there is no UI, so this is not recommended. You will have to be running jack in order to use this. diff --git a/delay_simple/c/HeavyContext.cpp b/delay_simple/c/HeavyContext.cpp new file mode 100644 index 0000000..88fa134 --- /dev/null +++ b/delay_simple/c/HeavyContext.cpp @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HeavyContext.hpp" +#include "HvTable.h" + +void defaultSendHook(HeavyContextInterface *context, + const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) { + HeavyContext *thisContext = reinterpret_cast(context); + const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage); + ReceiverMessagePair *p = reinterpret_cast(hLp_getWriteBuffer(&thisContext->outQueue, numBytes)); + if (p != nullptr) { + p->receiverHash = sendHash; + msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg)); + hLp_produce(&thisContext->outQueue, numBytes); + } else { + hv_assert(false && + "::defaultSendHook - The out message queue is full and cannot accept more messages until they " + "have been processed. Try increasing the outQueueKb size in the new_with_options() constructor."); + } +} + +HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) : + sampleRate(sampleRate) { + + hv_assert(sampleRate > 0.0); // sample rate must be positive + hv_assert(poolKb > 0); + hv_assert(inQueueKb > 0); + hv_assert(outQueueKb >= 0); + + blockStartTimestamp = 0; + printHook = nullptr; + userData = nullptr; + + // if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set. + // Otherwise outQueue and the sendhook are set to NULL. + sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr; + + HV_SPINLOCK_RELEASE(inQueueLock); + HV_SPINLOCK_RELEASE(outQueueLock); + + numBytes = sizeof(HeavyContext); + + numBytes += mq_initWithPoolSize(&mq, poolKb); + numBytes += hLp_init(&inQueue, inQueueKb * 1024); + numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL +} + +HeavyContext::~HeavyContext() { + mq_free(&mq); + hLp_free(&inQueue); + hLp_free(&outQueue); +} + +bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) { + HvMessage *m = HV_MESSAGE_ON_STACK(1); + msg_initWithBang(m, 0); + bool success = sendMessageToReceiver(receiverHash, 0.0, m); + return success; +} + +bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) { + HvMessage *m = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(m, 0, f); + bool success = sendMessageToReceiver(receiverHash, 0.0, m); + return success; +} + +bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) { + hv_assert(s != nullptr); + HvMessage *m = HV_MESSAGE_ON_STACK(1); + msg_initWithSymbol(m, 0, (char *) s); + bool success = sendMessageToReceiver(receiverHash, 0.0, m); + return success; +} + +bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) { + hv_assert(delayMs >= 0.0); + hv_assert(format != nullptr); + + va_list ap; + va_start(ap, format); + const int numElem = (int) hv_strlen(format); + HvMessage *m = HV_MESSAGE_ON_STACK(numElem); + msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0)); + for (int i = 0; i < numElem; i++) { + switch (format[i]) { + case 'b': msg_setBang(m, i); break; + case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; + case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; + case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; + default: break; + } + } + va_end(ap); + + bool success = sendMessageToReceiver(receiverHash, delayMs, m); + return success; +} + +bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) { + hv_assert(delayMs >= 0.0); + hv_assert(m != nullptr); + + const hv_uint32_t timestamp = blockStartTimestamp + + (hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0)); + + ReceiverMessagePair *p = nullptr; + HV_SPINLOCK_ACQUIRE(inQueueLock); + const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage); + p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes); + if (p != nullptr) { + p->receiverHash = receiverHash; + msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m)); + msg_setTimestamp(&p->msg, timestamp); + hLp_produce(&inQueue, numBytes); + } else { + hv_assert(false && + "::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they " + "have been processed. Try increasing the inQueueKb size in the new_with_options() constructor."); + } + HV_SPINLOCK_RELEASE(inQueueLock); + return (p != nullptr); +} + +bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + return mq_removeMessage(&mq, m, sendMessage); +} + +HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex) { + HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage); + return n; +} + +float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) { + HvTable *t = getTableForHash(tableHash); + if (t != nullptr) { + return hTable_getBuffer(t); + } else return nullptr; +} + +int HeavyContext::getLengthForTable(hv_uint32_t tableHash) { + HvTable *t = getTableForHash(tableHash); + if (t != nullptr) { + return hTable_getLength(t); + } else return 0; +} + +bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) { + HvTable *t = getTableForHash(tableHash); + if (t != nullptr) { + hTable_resize(t, newSampleLength); + return true; + } else return false; +} + +void HeavyContext::lockAcquire() { + HV_SPINLOCK_ACQUIRE(inQueueLock); +} + +bool HeavyContext::lockTry() { + HV_SPINLOCK_TRY(inQueueLock); +} + +void HeavyContext::lockRelease() { + HV_SPINLOCK_RELEASE(inQueueLock); +} + +void HeavyContext::setInputMessageQueueSize(int inQueueKb) { + hv_assert(inQueueKb > 0); + hLp_free(&inQueue); + hLp_init(&inQueue, inQueueKb*1024); +} + +void HeavyContext::setOutputMessageQueueSize(int outQueueKb) { + hv_assert(outQueueKb > 0); + hLp_free(&outQueue); + hLp_init(&outQueue, outQueueKb*1024); +} + +bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) { + *destinationHash = 0; + ReceiverMessagePair *p = nullptr; + hv_assert((sendHook == &defaultSendHook) && + "::getNextSentMessage - this function won't do anything if the msg outQueue " + "size is 0, or you've overriden the default sendhook."); + if (sendHook == &defaultSendHook) { + HV_SPINLOCK_ACQUIRE(outQueueLock); + if (hLp_hasData(&outQueue)) { + hv_uint32_t numBytes = 0; + p = reinterpret_cast(hLp_getReadBuffer(&outQueue, &numBytes)); + hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened."); + hv_assert(numBytes >= sizeof(ReceiverMessagePair)); + hv_assert((numBytes <= msgLengthBytes) && + "::getNextSentMessage - the sent message is bigger than the message " + "passed to handle it."); + *destinationHash = p->receiverHash; + hv_memcpy(outMsg, &p->msg, numBytes); + hLp_consume(&outQueue); + } + HV_SPINLOCK_RELEASE(outQueueLock); + } + return (p != nullptr); +} + +hv_uint32_t HeavyContext::getHashForString(const char *str) { + return hv_string_to_hash(str); +} + +HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { + hv_assert(c != nullptr); + return reinterpret_cast(c)->getTableForHash(tableHash); +} + +void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { + hv_assert(c != nullptr); + reinterpret_cast(c)->scheduleMessageForReceiver(receiverHash, m); +} + +HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex) { + hv_assert(c != nullptr); + HvMessage *n = reinterpret_cast(c)->scheduleMessageForObject( + m, sendMessage, letIndex); + return n; +} + +#ifdef __cplusplus +extern "C" { +#endif + +HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { + return _hv_table_get(c, tableHash); +} + +void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { + _hv_scheduleMessageForReceiver(c, receiverHash, m); +} + +HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex) { + return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex); +} + +#ifdef __cplusplus +} +#endif diff --git a/delay_simple/c/HeavyContext.hpp b/delay_simple/c/HeavyContext.hpp new file mode 100644 index 0000000..87b95d7 --- /dev/null +++ b/delay_simple/c/HeavyContext.hpp @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTEXT_H_ +#define _HEAVY_CONTEXT_H_ + +#include "HeavyContextInterface.hpp" +#include "HvLightPipe.h" +#include "HvMessageQueue.h" +#include "HvMath.h" + +struct HvTable; + +class HeavyContext : public HeavyContextInterface { + + public: + HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); + virtual ~HeavyContext(); + + int getSize() override { return (int) numBytes; } + + double getSampleRate() override { return sampleRate; } + + hv_uint32_t getCurrentSample() override { return blockStartTimestamp; } + float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); } + hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); } + + void setUserData(void *x) override { userData = x; } + void *getUserData() override { return userData; } + + // hook management + void setSendHook(HvSendHook_t *f) override { sendHook = f; } + HvSendHook_t *getSendHook() override { return sendHook; } + + void setPrintHook(HvPrintHook_t *f) override { printHook = f; } + HvPrintHook_t *getPrintHook() override { return printHook; } + + // message scheduling + bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override; + bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override; + bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override; + bool sendBangToReceiver(hv_uint32_t receiverHash) override; + bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override; + bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override; + + // table manipulation + float *getBufferForTable(hv_uint32_t tableHash) override; + int getLengthForTable(hv_uint32_t tableHash) override; + bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override; + + // lock control + void lockAcquire() override; + bool lockTry() override; + void lockRelease() override; + + // message queue management + void setInputMessageQueueSize(int inQueueKb) override; + void setOutputMessageQueueSize(int outQueueKb) override; + bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override; + + // utility functions + static hv_uint32_t getHashForString(const char *str); + + protected: + virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0; + friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t); + + virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0; + friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *); + + HvMessage *scheduleMessageForObject(const HvMessage *, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int); + friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int); + + friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *); + + // object state + double sampleRate; + hv_uint32_t blockStartTimestamp; + hv_size_t numBytes; + HvMessageQueue mq; + HvSendHook_t *sendHook; + HvPrintHook_t *printHook; + void *userData; + HvLightPipe inQueue; + HvLightPipe outQueue; + hv_atomic_bool inQueueLock; + hv_atomic_bool outQueueLock; +}; + +#endif // _HEAVY_CONTEXT_H_ diff --git a/delay_simple/c/HeavyContextInterface.hpp b/delay_simple/c/HeavyContextInterface.hpp new file mode 100644 index 0000000..a78fcbf --- /dev/null +++ b/delay_simple/c/HeavyContextInterface.hpp @@ -0,0 +1,291 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTEXT_INTERFACE_H_ +#define _HEAVY_CONTEXT_INTERFACE_H_ + +#include "HvUtils.h" + +#ifndef _HEAVY_DECLARATIONS_ +#define _HEAVY_DECLARATIONS_ + +class HeavyContextInterface; +struct HvMessage; + +typedef enum { + HV_PARAM_TYPE_PARAMETER_IN, + HV_PARAM_TYPE_PARAMETER_OUT, + HV_PARAM_TYPE_EVENT_IN, + HV_PARAM_TYPE_EVENT_OUT +} HvParameterType; + +typedef struct HvParameterInfo { + const char *name; // the human readable parameter name + hv_uint32_t hash; // an integer identified used by heavy for this parameter + HvParameterType type; // type of this parameter + float minVal; // the minimum value of this parameter + float maxVal; // the maximum value of this parameter + float defaultVal; // the default value of this parameter +} HvParameterInfo; + +typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); +typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); + +#endif // _HEAVY_DECLARATIONS_ + + + +class HeavyContextInterface { + + public: + HeavyContextInterface() {} + virtual ~HeavyContextInterface() {}; + + /** Returns the read-only user-assigned name of this patch. */ + virtual const char *getName() = 0; + + /** Returns the number of input channels with which this context has been configured. */ + virtual int getNumInputChannels() = 0; + + /** Returns the number of output channels with which this context has been configured. */ + virtual int getNumOutputChannels() = 0; + + /** + * Returns the total size in bytes of the context. + * This value may change if tables are resized. + */ + virtual int getSize() = 0; + + /** Returns the sample rate with which this context has been configured. */ + virtual double getSampleRate() = 0; + + /** Returns the current patch time in samples. This value is always exact. */ + virtual hv_uint32_t getCurrentSample() = 0; + virtual float samplesToMilliseconds(hv_uint32_t numSamples) = 0; + + /** Converts milliseconds to samples. Input is limited to non-negative range. */ + virtual hv_uint32_t millisecondsToSamples(float ms) = 0; + + /** Sets a user-definable value. This value is never manipulated by Heavy. */ + virtual void setUserData(void *x) = 0; + + /** Returns the user-defined data. */ + virtual void *getUserData() = 0; + + /** + * Set the send hook. The function is called whenever a message is sent to any send object. + * Messages returned by this function should NEVER be freed. If the message must persist, call + * hv_msg_copy() first. + */ + virtual void setSendHook(HvSendHook_t *f) = 0; + + /** Returns the send hook, or NULL if unset. */ + virtual HvSendHook_t *getSendHook() = 0; + + /** Set the print hook. The function is called whenever a message is sent to a print object. */ + virtual void setPrintHook(HvPrintHook_t *f) = 0; + + /** Returns the print hook, or NULL if unset. */ + virtual HvPrintHook_t *getPrintHook() = 0; + + /** + * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [[LLLL][RRRR]] + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ + virtual int process(float **inputBuffers, float **outputBuffer, int n) = 0; + + /** + * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LLLLRRRR] + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ + virtual int processInline(float *inputBuffers, float *outputBuffer, int n) = 0; + + /** + * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LRLRLRLR] + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ + virtual int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) = 0; + + /** + * Sends a formatted message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) = 0; + + /** + * Sends a formatted message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) = 0; + + /** + * A convenience function to send a float to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) = 0; + + /** + * A convenience function to send a bang to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendBangToReceiver(hv_uint32_t receiverHash) = 0; + + /** + * A convenience function to send a symbol to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) = 0; + + /** + * Cancels a previously scheduled message. + * + * @param sendMessage May be NULL. + */ + virtual bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)=nullptr) = 0; + + /** + * Returns information about each parameter such as name, hash, and range. + * The total number of parameters is always returned. + * + * @param index The parameter index. + * @param info A pointer to a HvParameterInfo struct. May be null. + * + * @return The total number of parameters. + */ + virtual int getParameterInfo(int index, HvParameterInfo *info) = 0; + + /** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ + virtual float *getBufferForTable(hv_uint32_t tableHash) = 0; + + /** Returns the length of this table in samples. */ + virtual int getLengthForTable(hv_uint32_t tableHash) = 0; + + /** + * Resizes the table to the given length. + * + * Existing contents are copied to the new table. Remaining space is cleared + * if the table is longer than the original, truncated otherwise. + * + * @param tableHash The table identifier. + * @param newSampleLength The new length of the table, in samples. + * + * @return False if the table could not be found. True otherwise. + */ + virtual bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) = 0; + + /** + * Acquire the input message queue lock. + * + * This function will block until the message lock as been acquired. + * Typical applications will not require the use of this function. + */ + virtual void lockAcquire() = 0; + + /** + * Try to acquire the input message queue lock. + * + * If the lock has been acquired, hv_lock_release() must be called to release it. + * Typical applications will not require the use of this function. + * + * @return Returns true if the lock has been acquired, false otherwise. + */ + virtual bool lockTry() = 0; + + /** + * Release the input message queue lock. + * + * Typical applications will not require the use of this function. + */ + virtual void lockRelease() = 0; + + /** + * Set the size of the input message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * + * @param inQueueKb Must be positive i.e. at least one. + */ + virtual void setInputMessageQueueSize(int inQueueKb) = 0; + + /** + * Set the size of the output message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * Only the default sendhook uses the outgoing message queue. If the default + * sendhook is not being used, then this function is not useful. + * + * @param outQueueKb Must be postive i.e. at least one. + */ + virtual void setOutputMessageQueueSize(int outQueueKb) = 0; + + /** + * Get the next message in the outgoing queue, will also consume the message. + * Returns false if there are no messages. + * + * @param destinationHash a hash of the name of the receiver the message was sent to. + * @param outMsg message pointer that is filled by the next message contents. + * @param msgLengthBytes max length of outMsg in bytes. + * + * @return True if there is a message in the outgoing queue. + */ + virtual bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) = 0; + + /** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ + static hv_uint32_t getHashForString(const char *str); +}; + +#endif // _HEAVY_CONTEXT_INTERFACE_H_ diff --git a/delay_simple/c/Heavy_delay_simple.cpp b/delay_simple/c/Heavy_delay_simple.cpp new file mode 100644 index 0000000..4191593 --- /dev/null +++ b/delay_simple/c/Heavy_delay_simple.cpp @@ -0,0 +1,659 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "Heavy_delay_simple.hpp" + +#include + +#define Context(_c) static_cast(_c) + + +/* + * C Functions + */ + +extern "C" { + HV_EXPORT HeavyContextInterface *hv_delay_simple_new(double sampleRate) { + // allocate aligned memory + void *ptr = hv_malloc(sizeof(Heavy_delay_simple)); + // ensure non-null + if (!ptr) return nullptr; + // call constructor + new(ptr) Heavy_delay_simple(sampleRate); + return Context(ptr); + } + + HV_EXPORT HeavyContextInterface *hv_delay_simple_new_with_options(double sampleRate, + int poolKb, int inQueueKb, int outQueueKb) { + // allocate aligned memory + void *ptr = hv_malloc(sizeof(Heavy_delay_simple)); + // ensure non-null + if (!ptr) return nullptr; + // call constructor + new(ptr) Heavy_delay_simple(sampleRate, poolKb, inQueueKb, outQueueKb); + return Context(ptr); + } + + HV_EXPORT void hv_delay_simple_free(HeavyContextInterface *instance) { + // call destructor + Context(instance)->~Heavy_delay_simple(); + // free memory + hv_free(instance); + } +} // extern "C" + + + + + + + +/* + * Class Functions + */ + +Heavy_delay_simple::Heavy_delay_simple(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) + : HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) { + numBytes += sTabread_init(&sTabread_OJVVdLuf, &hTable_TFqOKFso, true); + numBytes += sTabwrite_init(&sTabwrite_kTfJPrs1, &hTable_TFqOKFso); + numBytes += cVar_init_f(&cVar_4cpXTaBY, 0.0f); + numBytes += cVar_init_f(&cVar_fRPZcVCt, 0.0f); + numBytes += cVar_init_f(&cVar_Om7ifKfd, 0.0f); + numBytes += cVar_init_f(&cVar_LnHfGF8V, 0.0f); + numBytes += cVar_init_f(&cVar_mOyup5ir, 0.0f); + numBytes += cDelay_init(this, &cDelay_OFD5iAqe, 0.0f); + numBytes += cDelay_init(this, &cDelay_EI4kKdvf, 0.0f); + numBytes += hTable_init(&hTable_TFqOKFso, 256); + numBytes += cTabhead_init(&cTabhead_zhu6BFdy, &hTable_TFqOKFso); + numBytes += cVar_init_s(&cVar_nONxuBGP, "del-delay1012"); + numBytes += cDelay_init(this, &cDelay_FTWI9Lbr, 0.0f); + numBytes += cDelay_init(this, &cDelay_4Y58Ksfn, 0.0f); + numBytes += cBinop_init(&cBinop_kd9uMvnd, 0.0f); // __mul + numBytes += cBinop_init(&cBinop_99kJjPmN, 0.0f); // __sub + numBytes += cBinop_init(&cBinop_khIQbSXe, 0.0f); // __max + numBytes += cBinop_init(&cBinop_HsqyfMPx, 0.0f); // __sub + numBytes += sVarf_init(&sVarf_DXN3F4mN, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_o7lB6W7R, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_4RDRwlaL, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_vL8TEPN9, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_eLw9NCKn, 0.0f, 0.0f, false); + + // schedule a message to trigger all loadbangs via the __hv_init receiver + scheduleMessageForReceiver(0xCE5CC65B, msg_initWithBang(HV_MESSAGE_ON_STACK(1), 0)); +} + +Heavy_delay_simple::~Heavy_delay_simple() { + hTable_free(&hTable_TFqOKFso); +} + +HvTable *Heavy_delay_simple::getTableForHash(hv_uint32_t tableHash) {switch (tableHash) { + case 0x4678FF81: return &hTable_TFqOKFso; // del-delay1012 + default: return nullptr; + } +} + +void Heavy_delay_simple::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) { + switch (receiverHash) { + case 0x36377AA9: { // Delay_Feedback + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_LQwqfjMo_sendMessage); + break; + } + case 0xEE78D101: { // Delay_Time + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_UQL9KHRi_sendMessage); + break; + } + case 0x5EFB46D2: { // Dry_Volume + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_SIoX2X2C_sendMessage); + break; + } + case 0x677821DA: { // Gain + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_eGza5oBR_sendMessage); + break; + } + case 0xEA49B5A: { // Wet_Volume + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_ex2b75Oc_sendMessage); + break; + } + case 0xCE5CC65B: { // __hv_init + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_AJBhI0kX_sendMessage); + break; + } + case 0xD8A3CB65: { // dfeed1 + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_iG7Q7Uzr_sendMessage); + break; + } + case 0xAEFD47D3: { // dryvol + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_kJYDDgog_sendMessage); + break; + } + case 0x37BBAC62: { // dtime1 + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_kE9u8lIG_sendMessage); + break; + } + case 0xCA9AE94A: { // dvol1 + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_pzAN0z6v_sendMessage); + break; + } + default: return; + } +} + +int Heavy_delay_simple::getParameterInfo(int index, HvParameterInfo *info) { + if (info != nullptr) { + switch (index) { + case 0: { + info->name = "Delay_Feedback"; + info->hash = 0x36377AA9; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.25f; + break; + } + case 1: { + info->name = "Delay_Time"; + info->hash = 0xEE78D101; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 5000.0f; + info->defaultVal = 500.0f; + break; + } + case 2: { + info->name = "Dry_Volume"; + info->hash = 0x5EFB46D2; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.75f; + break; + } + case 3: { + info->name = "Gain"; + info->hash = 0x677821DA; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.75f; + break; + } + case 4: { + info->name = "Wet_Volume"; + info->hash = 0xEA49B5A; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.75f; + break; + } + default: { + info->name = "invalid parameter index"; + info->hash = 0; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 0.0f; + info->defaultVal = 0.0f; + break; + } + } + } + return 5; +} + + + +/* + * Send Function Implementations + */ + + +void Heavy_delay_simple::cVar_4cpXTaBY_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_wldZJDJM_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cVar_fRPZcVCt_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_dFhbvIPr_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cVar_Om7ifKfd_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_4A35hG1G_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cVar_LnHfGF8V_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_eLw9NCKn, m); +} + +void Heavy_delay_simple::cVar_mOyup5ir_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_jiugLxbD_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cMsg_hdrV3jKk_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "samplerate"); + cSystem_onMessage(_c, NULL, 0, m, &cSystem_B288dglo_sendMessage); +} + +void Heavy_delay_simple::cSystem_B288dglo_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_DIVIDE, 1000.0f, 0, m, &cBinop_NAf7xjjy_sendMessage); +} + +void Heavy_delay_simple::cDelay_OFD5iAqe_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_OFD5iAqe, m); + cDelay_onMessage(_c, &Context(_c)->cDelay_EI4kKdvf, 0, m, &cDelay_EI4kKdvf_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_OFD5iAqe, 0, m, &cDelay_OFD5iAqe_sendMessage); + sTabwrite_onMessage(_c, &Context(_c)->sTabwrite_kTfJPrs1, 1, m, NULL); +} + +void Heavy_delay_simple::cDelay_EI4kKdvf_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_EI4kKdvf, m); + cMsg_o1Fk4mWr_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cBinop_4MyuHNhK_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_clwxczYs_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::hTable_TFqOKFso_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_DlDeZMFB_sendMessage(_c, 0, m); + cDelay_onMessage(_c, &Context(_c)->cDelay_OFD5iAqe, 2, m, &cDelay_OFD5iAqe_sendMessage); + cCast_onMessage(_c, HV_CAST_BANG, 0, m, &cCast_g87DeymK_sendMessage); +} + +void Heavy_delay_simple::cMsg_clwxczYs_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(2); + msg_init(m, 2, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "resize"); + msg_setElementToFrom(m, 1, n, 0); + hTable_onMessage(_c, &Context(_c)->hTable_TFqOKFso, 0, m, &hTable_TFqOKFso_sendMessage); +} + +void Heavy_delay_simple::cBinop_NAf7xjjy_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_MULTIPLY, 5000.0f, 0, m, &cBinop_4MyuHNhK_sendMessage); +} + +void Heavy_delay_simple::cMsg_o1Fk4mWr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "mirror"); + hTable_onMessage(_c, &Context(_c)->hTable_TFqOKFso, 0, m, &hTable_TFqOKFso_sendMessage); +} + +void Heavy_delay_simple::cCast_g87DeymK_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cDelay_onMessage(_c, &Context(_c)->cDelay_OFD5iAqe, 0, m, &cDelay_OFD5iAqe_sendMessage); +} + +void Heavy_delay_simple::cMsg_DlDeZMFB_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setFloat(m, 0, static_cast(HV_N_SIMD)); + cDelay_onMessage(_c, &Context(_c)->cDelay_EI4kKdvf, 2, m, &cDelay_EI4kKdvf_sendMessage); +} + +void Heavy_delay_simple::cTabhead_zhu6BFdy_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_99kJjPmN, HV_BINOP_SUBTRACT, 0, m, &cBinop_99kJjPmN_sendMessage); +} + +void Heavy_delay_simple::cMsg_c68iDIVD_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "samplerate"); + cSystem_onMessage(_c, NULL, 0, m, &cSystem_LlBI3RTT_sendMessage); +} + +void Heavy_delay_simple::cSystem_LlBI3RTT_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_DIVIDE, 1000.0f, 0, m, &cBinop_qp875S9R_sendMessage); +} + +void Heavy_delay_simple::cVar_nONxuBGP_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_2Y6gSDxh_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cDelay_FTWI9Lbr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_FTWI9Lbr, m); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 0, m, &cDelay_4Y58Ksfn_sendMessage); + sTabread_onMessage(_c, &Context(_c)->sTabread_OJVVdLuf, 0, m, &sTabread_OJVVdLuf_sendMessage); +} + +void Heavy_delay_simple::cDelay_4Y58Ksfn_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_4Y58Ksfn, m); + sTabread_onMessage(_c, &Context(_c)->sTabread_OJVVdLuf, 0, m, &sTabread_OJVVdLuf_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 0, m, &cDelay_4Y58Ksfn_sendMessage); +} + +void Heavy_delay_simple::sTabread_OJVVdLuf_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + switch (letIn) { + case 0: { + break; + } + case 1: { + cBinop_onMessage(_c, &Context(_c)->cBinop_HsqyfMPx, HV_BINOP_SUBTRACT, 0, m, &cBinop_HsqyfMPx_sendMessage); + break; + } + default: return; + } +} + +void Heavy_delay_simple::cBinop_kd9uMvnd_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_khIQbSXe, HV_BINOP_MAX, 0, m, &cBinop_khIQbSXe_sendMessage); +} + +void Heavy_delay_simple::cBinop_qp875S9R_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_kd9uMvnd, HV_BINOP_MULTIPLY, 0, m, &cBinop_kd9uMvnd_sendMessage); +} + +void Heavy_delay_simple::cBinop_99kJjPmN_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_60hMCcpx_sendMessage(_c, 0, m); + sTabread_onMessage(_c, &Context(_c)->sTabread_OJVVdLuf, 0, m, &sTabread_OJVVdLuf_sendMessage); + cCast_onMessage(_c, HV_CAST_BANG, 0, m, &cCast_zoAHkSwC_sendMessage); +} + +void Heavy_delay_simple::cSystem_kPqFOU4H_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_HsqyfMPx, HV_BINOP_SUBTRACT, 1, m, &cBinop_HsqyfMPx_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 2, m, &cDelay_4Y58Ksfn_sendMessage); +} + +void Heavy_delay_simple::cMsg_2Y6gSDxh_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(3); + msg_init(m, 3, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "table"); + msg_setElementToFrom(m, 1, n, 0); + msg_setSymbol(m, 2, "size"); + cSystem_onMessage(_c, NULL, 0, m, &cSystem_kPqFOU4H_sendMessage); +} + +void Heavy_delay_simple::cMsg_60hMCcpx_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "clear"); + cDelay_onMessage(_c, &Context(_c)->cDelay_FTWI9Lbr, 0, m, &cDelay_FTWI9Lbr_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 0, m, &cDelay_4Y58Ksfn_sendMessage); +} + +void Heavy_delay_simple::cMsg_2y7SxePc_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setFloat(m, 0, static_cast(HV_N_SIMD)); + cBinop_onMessage(_c, &Context(_c)->cBinop_khIQbSXe, HV_BINOP_MAX, 1, m, &cBinop_khIQbSXe_sendMessage); +} + +void Heavy_delay_simple::cBinop_khIQbSXe_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_99kJjPmN, HV_BINOP_SUBTRACT, 1, m, &cBinop_99kJjPmN_sendMessage); +} + +void Heavy_delay_simple::cCast_zoAHkSwC_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cDelay_onMessage(_c, &Context(_c)->cDelay_FTWI9Lbr, 0, m, &cDelay_FTWI9Lbr_sendMessage); +} + +void Heavy_delay_simple::cBinop_H69XkGIL_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cDelay_onMessage(_c, &Context(_c)->cDelay_FTWI9Lbr, 2, m, &cDelay_FTWI9Lbr_sendMessage); +} + +void Heavy_delay_simple::cBinop_HsqyfMPx_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_MULTIPLY, -1.0f, 0, m, &cBinop_H69XkGIL_sendMessage); +} + +void Heavy_delay_simple::cCast_spzeS2Y3_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_nONxuBGP, 0, m, &cVar_nONxuBGP_sendMessage); + cMsg_c68iDIVD_sendMessage(_c, 0, m); + cTabhead_onMessage(_c, &Context(_c)->cTabhead_zhu6BFdy, 0, m, &cTabhead_zhu6BFdy_sendMessage); +} + +void Heavy_delay_simple::cSend_wldZJDJM_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_kE9u8lIG_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cSend_dFhbvIPr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_pzAN0z6v_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cSend_4A35hG1G_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_iG7Q7Uzr_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cSend_jiugLxbD_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_kJYDDgog_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cReceive_kE9u8lIG_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_kd9uMvnd, HV_BINOP_MULTIPLY, 1, m, &cBinop_kd9uMvnd_sendMessage); + cCast_onMessage(_c, HV_CAST_BANG, 0, m, &cCast_spzeS2Y3_sendMessage); +} + +void Heavy_delay_simple::cReceive_pzAN0z6v_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_o7lB6W7R, m); +} + +void Heavy_delay_simple::cReceive_iG7Q7Uzr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_4RDRwlaL, m); +} + +void Heavy_delay_simple::cReceive_kJYDDgog_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_vL8TEPN9, m); +} + +void Heavy_delay_simple::cReceive_AJBhI0kX_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_hdrV3jKk_sendMessage(_c, 0, m); + cMsg_2y7SxePc_sendMessage(_c, 0, m); + cVar_onMessage(_c, &Context(_c)->cVar_nONxuBGP, 0, m, &cVar_nONxuBGP_sendMessage); + cMsg_c68iDIVD_sendMessage(_c, 0, m); + cTabhead_onMessage(_c, &Context(_c)->cTabhead_zhu6BFdy, 0, m, &cTabhead_zhu6BFdy_sendMessage); +} + +void Heavy_delay_simple::cReceive_eGza5oBR_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_LnHfGF8V, 0, m, &cVar_LnHfGF8V_sendMessage); +} + +void Heavy_delay_simple::cReceive_SIoX2X2C_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_mOyup5ir, 0, m, &cVar_mOyup5ir_sendMessage); +} + +void Heavy_delay_simple::cReceive_UQL9KHRi_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_4cpXTaBY, 0, m, &cVar_4cpXTaBY_sendMessage); +} + +void Heavy_delay_simple::cReceive_ex2b75Oc_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_fRPZcVCt, 0, m, &cVar_fRPZcVCt_sendMessage); +} + +void Heavy_delay_simple::cReceive_LQwqfjMo_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_Om7ifKfd, 0, m, &cVar_Om7ifKfd_sendMessage); +} + + + + +/* + * Context Process Implementation + */ + +int Heavy_delay_simple::process(float **inputBuffers, float **outputBuffers, int n) { + while (hLp_hasData(&inQueue)) { + hv_uint32_t numBytes = 0; + ReceiverMessagePair *p = reinterpret_cast(hLp_getReadBuffer(&inQueue, &numBytes)); + hv_assert(numBytes >= sizeof(ReceiverMessagePair)); + scheduleMessageForReceiver(p->receiverHash, &p->msg); + hLp_consume(&inQueue); + } + const int n4 = n & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD + + // temporary signal vars + hv_bufferf_t Bf0, Bf1, Bf2, Bf3; + + // input and output vars + hv_bufferf_t O0, O1; + hv_bufferf_t I0, I1; + + // declare and init the zero buffer + hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO)); + + hv_uint32_t nextBlock = blockStartTimestamp; + for (int n = 0; n < n4; n += HV_N_SIMD) { + + // process all of the messages for this block + nextBlock += HV_N_SIMD; + while (mq_hasMessageBefore(&mq, nextBlock)) { + MessageNode *const node = mq_peek(&mq); + node->sendMessage(this, node->let, node->m); + mq_pop(&mq); + } + + // load input buffers + __hv_load_f(inputBuffers[0]+n, VOf(I0)); + __hv_load_f(inputBuffers[1]+n, VOf(I1)); + + // zero output buffers + __hv_zero_f(VOf(O0)); + __hv_zero_f(VOf(O1)); + + // process all signal functions + __hv_add_f(VIf(I0), VIf(I1), VOf(Bf0)); + __hv_varread_f(&sVarf_eLw9NCKn, VOf(Bf1)); + __hv_mul_f(VIf(Bf0), VIf(Bf1), VOf(Bf1)); + __hv_tabread_f(&sTabread_OJVVdLuf, VOf(Bf0)); + __hv_varread_f(&sVarf_o7lB6W7R, VOf(Bf2)); + __hv_varread_f(&sVarf_vL8TEPN9, VOf(Bf3)); + __hv_mul_f(VIf(Bf1), VIf(Bf3), VOf(Bf3)); + __hv_fma_f(VIf(Bf0), VIf(Bf2), VIf(Bf3), VOf(Bf3)); + __hv_varread_f(&sVarf_DXN3F4mN, VOf(Bf2)); + __hv_add_f(VIf(Bf1), VIf(Bf2), VOf(Bf2)); + __hv_tabwrite_f(&sTabwrite_kTfJPrs1, VIf(Bf2)); + __hv_varread_f(&sVarf_4RDRwlaL, VOf(Bf2)); + __hv_mul_f(VIf(Bf0), VIf(Bf2), VOf(Bf2)); + __hv_varwrite_f(&sVarf_DXN3F4mN, VIf(Bf2)); + __hv_add_f(VIf(Bf3), VIf(O0), VOf(O0)); + __hv_add_f(VIf(Bf3), VIf(O1), VOf(O1)); + + // save output vars to output buffer + __hv_store_f(outputBuffers[0]+n, VIf(O0)); + __hv_store_f(outputBuffers[1]+n, VIf(O1)); + } + + blockStartTimestamp = nextBlock; + + return n4; // return the number of frames processed +} + +int Heavy_delay_simple::processInline(float *inputBuffers, float *outputBuffers, int n4) { + hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD + + // define the heavy input buffer for 2 channel(s) + float **const bIn = reinterpret_cast(hv_alloca(2*sizeof(float *))); + bIn[0] = inputBuffers+(0*n4); + bIn[1] = inputBuffers+(1*n4); + + // define the heavy output buffer for 2 channel(s) + float **const bOut = reinterpret_cast(hv_alloca(2*sizeof(float *))); + bOut[0] = outputBuffers+(0*n4); + bOut[1] = outputBuffers+(1*n4); + + int n = process(bIn, bOut, n4); + return n; +} + +int Heavy_delay_simple::processInlineInterleaved(float *inputBuffers, float *outputBuffers, int n4) { + hv_assert(n4 & ~HV_N_SIMD_MASK); // ensure that n4 is a multiple of HV_N_SIMD + + // define the heavy input buffer for 2 channel(s), uninterleave + float *const bIn = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); + #if HV_SIMD_SSE || HV_SIMD_AVX + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + __m128 a = _mm_load_ps(inputBuffers+i); // LRLR + __m128 b = _mm_load_ps(inputBuffers+4+i); // LRLR + __m128 x = _mm_shuffle_ps(a, b, _MM_SHUFFLE(2,0,2,0)); // LLLL + __m128 y = _mm_shuffle_ps(a, b, _MM_SHUFFLE(3,1,3,1)); // RRRR + _mm_store_ps(bIn+j, x); + _mm_store_ps(bIn+n4+j, y); + } + #elif HV_SIMD_NEON + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + float32x4x2_t a = vld2q_f32(inputBuffers+i); // load and uninterleave + vst1q_f32(bIn+j, a.val[0]); + vst1q_f32(bIn+n4+j, a.val[1]); + } + #else // HV_SIMD_NONE + for (int j = 0; j < n4; ++j) { + bIn[0*n4+j] = inputBuffers[0+2*j]; + bIn[1*n4+j] = inputBuffers[1+2*j]; + } + #endif + + // define the heavy output buffer for 2 channel(s) + float *const bOut = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); + + int n = processInline(bIn, bOut, n4); + + // interleave the heavy output into the output buffer + #if HV_SIMD_AVX + for (int i = 0, j = 0; j < n4; j += 8, i += 16) { + __m256 x = _mm256_load_ps(bOut+j); // LLLLLLLL + __m256 y = _mm256_load_ps(bOut+n4+j); // RRRRRRRR + __m256 a = _mm256_unpacklo_ps(x, y); // LRLRLRLR + __m256 b = _mm256_unpackhi_ps(x, y); // LRLRLRLR + _mm256_store_ps(outputBuffers+i, a); + _mm256_store_ps(outputBuffers+8+i, b); + } + #elif HV_SIMD_SSE + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + __m128 x = _mm_load_ps(bOut+j); // LLLL + __m128 y = _mm_load_ps(bOut+n4+j); // RRRR + __m128 a = _mm_unpacklo_ps(x, y); // LRLR + __m128 b = _mm_unpackhi_ps(x, y); // LRLR + _mm_store_ps(outputBuffers+i, a); + _mm_store_ps(outputBuffers+4+i, b); + } + #elif HV_SIMD_NEON + // https://community.arm.com/groups/processors/blog/2012/03/13/coding-for-neon--part-5-rearranging-vectors + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + float32x4_t x = vld1q_f32(bOut+j); + float32x4_t y = vld1q_f32(bOut+n4+j); + float32x4x2_t z = {x, y}; + vst2q_f32(outputBuffers+i, z); // interleave and store + } + #else // HV_SIMD_NONE + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < n4; ++j) { + outputBuffers[i+2*j] = bOut[i*n4+j]; + } + } + #endif + + return n; +} diff --git a/delay_simple/c/Heavy_delay_simple.h b/delay_simple/c/Heavy_delay_simple.h new file mode 100644 index 0000000..6c68048 --- /dev/null +++ b/delay_simple/c/Heavy_delay_simple.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HEAVY_DELAY_SIMPLE_H_ +#define _HEAVY_DELAY_SIMPLE_H_ + +#include "HvHeavy.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if HV_APPLE +#pragma mark - Heavy Context +#endif + +typedef enum { + HV_DELAY_SIMPLE_PARAM_IN_DELAY_FEEDBACK = 0x36377AA9, // Delay_Feedback + HV_DELAY_SIMPLE_PARAM_IN_DELAY_TIME = 0xEE78D101, // Delay_Time + HV_DELAY_SIMPLE_PARAM_IN_DRY_VOLUME = 0x5EFB46D2, // Dry_Volume + HV_DELAY_SIMPLE_PARAM_IN_GAIN = 0x677821DA, // Gain + HV_DELAY_SIMPLE_PARAM_IN_WET_VOLUME = 0xEA49B5A, // Wet_Volume +} Hv_delay_simple_ParameterIn; + + +/** + * Creates a new patch instance. + * Sample rate should be positive and in Hertz, e.g. 44100.0. + */ +HeavyContextInterface *hv_delay_simple_new(double sampleRate); + +/** + * Creates a new patch instance. + * @param sampleRate Sample rate should be positive (> 0) and in Hertz, e.g. 48000.0. + * @param poolKb Pool size is in kilobytes, and determines the maximum amount of memory + * allocated to messages at any time. By default this is 10 KB. + * @param inQueueKb The size of the input message queue in kilobytes. It determines the + * amount of memory dedicated to holding scheduled messages between calls to + * process(). Default is 2 KB. + * @param outQueueKb The size of the output message queue in kilobytes. It determines the + * amount of memory dedicated to holding scheduled messages to the default sendHook. + * See getNextSentMessage() for info on accessing these messages. Default is 0 KB. + */ +HeavyContextInterface *hv_delay_simple_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb); + +/** + * Free the patch instance. + */ +void hv_delay_simple_free(HeavyContextInterface *instance); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_DELAY_SIMPLE_H_ diff --git a/delay_simple/c/Heavy_delay_simple.hpp b/delay_simple/c/Heavy_delay_simple.hpp new file mode 100644 index 0000000..8e1f353 --- /dev/null +++ b/delay_simple/c/Heavy_delay_simple.hpp @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HEAVY_CONTEXT_DELAY_SIMPLE_HPP_ +#define _HEAVY_CONTEXT_DELAY_SIMPLE_HPP_ + +// object includes +#include "HeavyContext.hpp" +#include "HvControlTabhead.h" +#include "HvSignalVar.h" +#include "HvTable.h" +#include "HvControlBinop.h" +#include "HvSignalTabwrite.h" +#include "HvControlDelay.h" +#include "HvMath.h" +#include "HvSignalTabread.h" +#include "HvControlSystem.h" +#include "HvControlVar.h" +#include "HvControlCast.h" + +class Heavy_delay_simple : public HeavyContext { + + public: + Heavy_delay_simple(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); + ~Heavy_delay_simple(); + + const char *getName() override { return "delay_simple"; } + int getNumInputChannels() override { return 2; } + int getNumOutputChannels() override { return 2; } + + int process(float **inputBuffers, float **outputBuffer, int n) override; + int processInline(float *inputBuffers, float *outputBuffer, int n) override; + int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) override; + + int getParameterInfo(int index, HvParameterInfo *info) override; + struct Parameter { + struct In { + enum ParameterIn : hv_uint32_t { + DELAY_FEEDBACK = 0x36377AA9, // Delay_Feedback + DELAY_TIME = 0xEE78D101, // Delay_Time + DRY_VOLUME = 0x5EFB46D2, // Dry_Volume + GAIN = 0x677821DA, // Gain + WET_VOLUME = 0xEA49B5A, // Wet_Volume + }; + }; + }; + + private: + HvTable *getTableForHash(hv_uint32_t tableHash) override; + void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override; + + // static sendMessage functions + static void cVar_4cpXTaBY_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_fRPZcVCt_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_Om7ifKfd_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_LnHfGF8V_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_mOyup5ir_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_hdrV3jKk_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSystem_B288dglo_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_OFD5iAqe_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_EI4kKdvf_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_4MyuHNhK_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void hTable_TFqOKFso_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_clwxczYs_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_NAf7xjjy_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_o1Fk4mWr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cCast_g87DeymK_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_DlDeZMFB_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cTabhead_zhu6BFdy_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_c68iDIVD_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSystem_LlBI3RTT_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_nONxuBGP_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_FTWI9Lbr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_4Y58Ksfn_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void sTabread_OJVVdLuf_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_kd9uMvnd_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_qp875S9R_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_99kJjPmN_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSystem_kPqFOU4H_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_2Y6gSDxh_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_60hMCcpx_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_2y7SxePc_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_khIQbSXe_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cCast_zoAHkSwC_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_H69XkGIL_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_HsqyfMPx_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cCast_spzeS2Y3_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_wldZJDJM_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_dFhbvIPr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_4A35hG1G_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_jiugLxbD_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_kE9u8lIG_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_pzAN0z6v_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_iG7Q7Uzr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_kJYDDgog_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_AJBhI0kX_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_eGza5oBR_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_SIoX2X2C_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_UQL9KHRi_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_ex2b75Oc_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_LQwqfjMo_sendMessage(HeavyContextInterface *, int, const HvMessage *); + + // objects + SignalTabread sTabread_OJVVdLuf; + SignalTabwrite sTabwrite_kTfJPrs1; + ControlVar cVar_4cpXTaBY; + ControlVar cVar_fRPZcVCt; + ControlVar cVar_Om7ifKfd; + ControlVar cVar_LnHfGF8V; + ControlVar cVar_mOyup5ir; + ControlDelay cDelay_OFD5iAqe; + ControlDelay cDelay_EI4kKdvf; + ControlBinop cBinop_4MyuHNhK; + HvTable hTable_TFqOKFso; + ControlBinop cBinop_NAf7xjjy; + ControlTabhead cTabhead_zhu6BFdy; + ControlVar cVar_nONxuBGP; + ControlDelay cDelay_FTWI9Lbr; + ControlDelay cDelay_4Y58Ksfn; + ControlBinop cBinop_kd9uMvnd; + ControlBinop cBinop_qp875S9R; + ControlBinop cBinop_99kJjPmN; + ControlBinop cBinop_khIQbSXe; + ControlBinop cBinop_H69XkGIL; + ControlBinop cBinop_HsqyfMPx; + SignalVarf sVarf_DXN3F4mN; + SignalVarf sVarf_o7lB6W7R; + SignalVarf sVarf_4RDRwlaL; + SignalVarf sVarf_vL8TEPN9; + SignalVarf sVarf_eLw9NCKn; +}; + +#endif // _HEAVY_CONTEXT_DELAY_SIMPLE_HPP_ diff --git a/delay_simple/c/HvControlBinop.c b/delay_simple/c/HvControlBinop.c new file mode 100644 index 0000000..cc248d9 --- /dev/null +++ b/delay_simple/c/HvControlBinop.c @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlBinop.h" + +hv_size_t cBinop_init(ControlBinop *o, float k) { + o->k = k; + return 0; +} + +static float cBinop_perform_op(BinopType op, float f, float k) { + switch (op) { + case HV_BINOP_ADD: return f + k; + case HV_BINOP_SUBTRACT: return f - k; + case HV_BINOP_MULTIPLY: return f * k; + case HV_BINOP_DIVIDE: return (k != 0.0f) ? (f/k) : 0.0f; + case HV_BINOP_INT_DIV: { + const int ik = (int) k; + return (ik != 0) ? (float) (((int) f) / ik) : 0.0f; + } + case HV_BINOP_MOD_BIPOLAR: { + const int ik = (int) k; + return (ik != 0) ? (float) (((int) f) % ik) : 0.0f; + } + case HV_BINOP_MOD_UNIPOLAR: { + f = (k == 0.0f) ? 0.0f : (float) ((int) f % (int) k); + return (f < 0.0f) ? f + hv_abs_f(k) : f; + } + case HV_BINOP_BIT_LEFTSHIFT: return (float) (((int) f) << ((int) k)); + case HV_BINOP_BIT_RIGHTSHIFT: return (float) (((int) f) >> ((int) k)); + case HV_BINOP_BIT_AND: return (float) ((int) f & (int) k); + case HV_BINOP_BIT_XOR: return (float) ((int) f ^ (int) k); + case HV_BINOP_BIT_OR: return (float) ((int) f | (int) k); + case HV_BINOP_EQ: return (f == k) ? 1.0f : 0.0f; + case HV_BINOP_NEQ: return (f != k) ? 1.0f : 0.0f; + case HV_BINOP_LOGICAL_AND: return ((f == 0.0f) || (k == 0.0f)) ? 0.0f : 1.0f; + case HV_BINOP_LOGICAL_OR: return ((f == 0.0f) && (k == 0.0f)) ? 0.0f : 1.0f; + case HV_BINOP_LESS_THAN: return (f < k) ? 1.0f : 0.0f; + case HV_BINOP_LESS_THAN_EQL: return (f <= k) ? 1.0f : 0.0f; + case HV_BINOP_GREATER_THAN: return (f > k) ? 1.0f : 0.0f; + case HV_BINOP_GREATER_THAN_EQL: return (f >= k) ? 1.0f : 0.0f; + case HV_BINOP_MAX: return hv_max_f(f, k); + case HV_BINOP_MIN: return hv_min_f(f, k); + case HV_BINOP_POW: return (f > 0.0f) ? hv_pow_f(f, k) : 0.0f; + case HV_BINOP_ATAN2: return ((f == 0.0f) && (k == 0.0f)) ? 0.0f : hv_atan2_f(f, k); + default: return 0.0f; + } +} + +void cBinop_onMessage(HeavyContextInterface *_c, ControlBinop *o, BinopType op, int letIn, + const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (msg_isFloat(m, 0)) { + // Note(joe): supporting Pd's ability to perform operations of packs + // of floats is likely to not be supported in the future. + if (msg_isFloat(m, 1)) o->k = msg_getFloat(m, 1); + HvMessage *n = HV_MESSAGE_ON_STACK(1); + float f = cBinop_perform_op(op, msg_getFloat(m, 0), o->k); + msg_initWithFloat(n, msg_getTimestamp(m), f); + sendMessage(_c, 0, n); + } + break; + } + case 1: { + if (msg_isFloat(m, 0)) { + o->k = msg_getFloat(m, 0); + } + break; + } + default: break; + } +} + +void cBinop_k_onMessage(HeavyContextInterface *_c, void *o, BinopType op, float k, + int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (msg_isFloat(m, 0)) { + // NOTE(mhroth): Heavy does not support sending bangs to binop objects to return the previous output + float f = (msg_isFloat(m, 1)) ? msg_getFloat(m, 1) : k; + HvMessage *n = HV_MESSAGE_ON_STACK(1); + f = cBinop_perform_op(op, msg_getFloat(m, 0), f); + msg_initWithFloat(n, msg_getTimestamp(m), f); + sendMessage(_c, 0, n); + } +} diff --git a/delay_simple/c/HvControlBinop.h b/delay_simple/c/HvControlBinop.h new file mode 100644 index 0000000..e6d197a --- /dev/null +++ b/delay_simple/c/HvControlBinop.h @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_BINOP_H_ +#define _HEAVY_CONTROL_BINOP_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum BinopType { + HV_BINOP_ADD, + HV_BINOP_SUBTRACT, + HV_BINOP_MULTIPLY, + HV_BINOP_DIVIDE, + HV_BINOP_INT_DIV, + HV_BINOP_MOD_BIPOLAR, + HV_BINOP_MOD_UNIPOLAR, + HV_BINOP_BIT_LEFTSHIFT, + HV_BINOP_BIT_RIGHTSHIFT, + HV_BINOP_BIT_AND, + HV_BINOP_BIT_XOR, + HV_BINOP_BIT_OR, + HV_BINOP_EQ, + HV_BINOP_NEQ, + HV_BINOP_LOGICAL_AND, + HV_BINOP_LOGICAL_OR, + HV_BINOP_LESS_THAN, + HV_BINOP_LESS_THAN_EQL, + HV_BINOP_GREATER_THAN, + HV_BINOP_GREATER_THAN_EQL, + HV_BINOP_MAX, + HV_BINOP_MIN, + HV_BINOP_POW, + HV_BINOP_ATAN2 +} BinopType; + +typedef struct ControlBinop { + float k; +} ControlBinop; + +hv_size_t cBinop_init(ControlBinop *o, float k); + +void cBinop_onMessage(HeavyContextInterface *_c, ControlBinop *o, BinopType op, int letIn, + const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +void cBinop_k_onMessage(HeavyContextInterface *_c, void *o, BinopType op, float k, + int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_BINOP_H_ diff --git a/delay_simple/c/HvControlCast.c b/delay_simple/c/HvControlCast.c new file mode 100644 index 0000000..8bf3892 --- /dev/null +++ b/delay_simple/c/HvControlCast.c @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlCast.h" + +void cCast_onMessage(HeavyContextInterface *_c, CastType castType, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (castType) { + case HV_CAST_BANG: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithBang(n, msg_getTimestamp(m)); + sendMessage(_c, 0, n); + break; + } + case HV_CAST_FLOAT: { + if (msg_isFloat(m, 0)) { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), msg_getFloat(m, 0)); + sendMessage(_c, 0, n); + } + break; + } + case HV_CAST_SYMBOL: { + switch (msg_getType(m, 0)) { + case HV_MSG_BANG: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithSymbol(n, msg_getTimestamp(m), "bang"); + sendMessage(_c, 0, n); + break; + } + case HV_MSG_FLOAT: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithSymbol(n, msg_getTimestamp(m), "float"); + sendMessage(_c, 0, n); + break; + } + case HV_MSG_SYMBOL: { + sendMessage(_c, 0, m); + break; + } + default: return; + } + break; + } + default: return; + } +} diff --git a/delay_simple/c/HvControlCast.h b/delay_simple/c/HvControlCast.h new file mode 100644 index 0000000..1952f6d --- /dev/null +++ b/delay_simple/c/HvControlCast.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_CAST_H_ +#define _HEAVY_CONTROL_CAST_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum CastType { + HV_CAST_BANG, + HV_CAST_FLOAT, + HV_CAST_SYMBOL +} CastType; + +void cCast_onMessage(HeavyContextInterface *_c, CastType castType, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_CAST_H_ diff --git a/delay_simple/c/HvControlDelay.c b/delay_simple/c/HvControlDelay.c new file mode 100644 index 0000000..fd8aff9 --- /dev/null +++ b/delay_simple/c/HvControlDelay.c @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlDelay.h" + +hv_size_t cDelay_init(HeavyContextInterface *_c, ControlDelay *o, float delayMs) { + o->delay = hv_millisecondsToSamples(_c, delayMs); + hv_memclear(o->msgs, __HV_DELAY_MAX_MESSAGES*sizeof(HvMessage *)); + return 0; +} + +void cDelay_onMessage(HeavyContextInterface *_c, ControlDelay *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (msg_compareSymbol(m, 0, "flush")) { + // send all messages immediately + for (int i = 0; i < __HV_DELAY_MAX_MESSAGES; i++) { + HvMessage *n = o->msgs[i]; + if (n != NULL) { + msg_setTimestamp(n, msg_getTimestamp(m)); // update the timestamp to now + sendMessage(_c, 0, n); // send the message + hv_cancelMessage(_c, n, sendMessage); // then clear it + // NOTE(mhroth): there may be a problem here if a flushed message causes a clear message to return + // to this object in the same step + } + } + hv_memclear(o->msgs, __HV_DELAY_MAX_MESSAGES*sizeof(HvMessage *)); + } else if (msg_compareSymbol(m, 0, "clear")) { + // cancel (clear) all (pending) messages + for (int i = 0; i < __HV_DELAY_MAX_MESSAGES; i++) { + HvMessage *n = o->msgs[i]; + if (n != NULL) { + hv_cancelMessage(_c, n, sendMessage); + } + } + hv_memclear(o->msgs, __HV_DELAY_MAX_MESSAGES*sizeof(HvMessage *)); + } else { + hv_uint32_t ts = msg_getTimestamp(m); + msg_setTimestamp((HvMessage *) m, ts+o->delay); // update the timestamp to set the delay + int i; + for (i = 0; i < __HV_DELAY_MAX_MESSAGES; i++) { + if (o->msgs[i] == NULL) { + o->msgs[i] = hv_scheduleMessageForObject(_c, m, sendMessage, 0); + break; + } + } + hv_assert((i < __HV_DELAY_MAX_MESSAGES) && // scheduled message limit reached + "[__delay] cannot track any more messages. Try increasing the size of __HV_DELAY_MAX_MESSAGES."); + msg_setTimestamp((HvMessage *) m, ts); // return to the original timestamp + } + break; + } + case 1: { + if (msg_isFloat(m,0)) { + // set delay in milliseconds (cannot be negative!) + o->delay = hv_millisecondsToSamples(_c, msg_getFloat(m,0)); + } + break; + } + case 2: { + if (msg_isFloat(m,0)) { + // set delay in samples (cannot be negative!) + o->delay = (hv_uint32_t) hv_max_f(0.0f, msg_getFloat(m,0)); + } + break; + } + default: break; + } +} + +void cDelay_clearExecutingMessage(ControlDelay *o, const HvMessage *m) { + for (int i = 0; i < __HV_DELAY_MAX_MESSAGES; ++i) { + if (o->msgs[i] == m) { + o->msgs[i] = NULL; + break; + } + } +} diff --git a/delay_simple/c/HvControlDelay.h b/delay_simple/c/HvControlDelay.h new file mode 100644 index 0000000..b2943fb --- /dev/null +++ b/delay_simple/c/HvControlDelay.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_DELAY_H_ +#define _HEAVY_CONTROL_DELAY_H_ + +#define __HV_DELAY_MAX_MESSAGES 8 + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ControlDelay { + hv_uint32_t delay; // delay in samples + HvMessage *msgs[__HV_DELAY_MAX_MESSAGES]; +} ControlDelay; + +hv_size_t cDelay_init(HeavyContextInterface *_c, ControlDelay *o, float delayMs); + +void cDelay_onMessage(HeavyContextInterface *_c, ControlDelay *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +void cDelay_clearExecutingMessage(ControlDelay *o, const HvMessage *m); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_DELAY_H_ diff --git a/delay_simple/c/HvControlSystem.c b/delay_simple/c/HvControlSystem.c new file mode 100644 index 0000000..a1ad30f --- /dev/null +++ b/delay_simple/c/HvControlSystem.c @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlSystem.h" + +void cSystem_onMessage(HeavyContextInterface *_c, void *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + + HvMessage *n = HV_MESSAGE_ON_STACK(1); + if (msg_compareSymbol(m, 0, "samplerate")) { + + msg_initWithFloat(n, msg_getTimestamp(m), (float) hv_getSampleRate(_c)); + } else if (msg_compareSymbol(m, 0, "numInputChannels")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hv_getNumInputChannels(_c)); + } else if (msg_compareSymbol(m, 0, "numOutputChannels")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hv_getNumOutputChannels(_c)); + } else if (msg_compareSymbol(m, 0, "currentTime")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) msg_getTimestamp(m)); + } else if (msg_compareSymbol(m, 0, "table")) { + // NOTE(mhroth): no need to check message format for symbols as table lookup will fail otherwise + HvTable *table = hv_table_get(_c, msg_getHash(m,1)); + if (table != NULL) { + if (msg_compareSymbol(m, 2, "length")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getLength(table)); + } else if (msg_compareSymbol(m, 2, "size")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(table)); + } else if (msg_compareSymbol(m, 2, "head")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getHead(table)); + } else return; + } else return; + } else return; + sendMessage(_c, 0, n); +} diff --git a/delay_simple/c/HvControlSystem.h b/delay_simple/c/HvControlSystem.h new file mode 100644 index 0000000..42da7bd --- /dev/null +++ b/delay_simple/c/HvControlSystem.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_SYSTEM_H_ +#define _HEAVY_CONTROL_SYSTEM_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void cSystem_onMessage(HeavyContextInterface *_c, void *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_SYSTEM_H_ diff --git a/delay_simple/c/HvControlTabhead.c b/delay_simple/c/HvControlTabhead.c new file mode 100644 index 0000000..586ed78 --- /dev/null +++ b/delay_simple/c/HvControlTabhead.c @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlTabhead.h" + +hv_size_t cTabhead_init(ControlTabhead *o, HvTable *table) { + o->table = table; + return 0; +} + +void cTabhead_onMessage(HeavyContextInterface *_c, ControlTabhead *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (msg_getType(m,0) == HV_MSG_BANG) { + // get current head of table + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getHead(o->table)); + sendMessage(_c, 0, n); + } + break; + } + case 1: { + if (msg_isHashLike(m,0)) { + // set a new table + o->table = hv_table_get(_c, msg_getHash(m,0)); + } + break; + } + default: break; + } +} diff --git a/delay_simple/c/HvControlTabhead.h b/delay_simple/c/HvControlTabhead.h new file mode 100644 index 0000000..c65dadd --- /dev/null +++ b/delay_simple/c/HvControlTabhead.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_TABHEAD_H_ +#define _HEAVY_CONTROL_TABHEAD_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ControlTabhead { + struct HvTable *table; +} ControlTabhead; + +hv_size_t cTabhead_init(ControlTabhead *o, struct HvTable *table); + +void cTabhead_onMessage(HeavyContextInterface *_c, ControlTabhead *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_TABHEAD_H_ diff --git a/delay_simple/c/HvControlVar.c b/delay_simple/c/HvControlVar.c new file mode 100644 index 0000000..80f22b9 --- /dev/null +++ b/delay_simple/c/HvControlVar.c @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlVar.h" + +hv_size_t cVar_init_f(ControlVar *o, float k) { + o->e.type = HV_MSG_FLOAT; + o->e.data.f = k; + return 0; +} + +hv_size_t cVar_init_s(ControlVar *o, const char *s) { + o->e.type = HV_MSG_HASH; + o->e.data.h = hv_string_to_hash(s); + return 0; +} + +void cVar_free(ControlVar *o) { + // nothing to do +} + +void cVar_onMessage(HeavyContextInterface *_c, ControlVar *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + switch (msg_getType(m,0)) { + case HV_MSG_BANG: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + if (o->e.type == HV_MSG_FLOAT) msg_initWithFloat(n, msg_getTimestamp(m), o->e.data.f); + else if (o->e.type == HV_MSG_HASH) msg_initWithHash(n, msg_getTimestamp(m), o->e.data.h); + else return; + sendMessage(_c, 0, n); + break; + } + case HV_MSG_FLOAT: { + o->e.type = HV_MSG_FLOAT; + o->e.data.f = msg_getFloat(m,0); + sendMessage(_c, 0, m); + break; + } + case HV_MSG_SYMBOL: + case HV_MSG_HASH: { + o->e.type = HV_MSG_HASH; + o->e.data.h = msg_getHash(m,0); + sendMessage(_c, 0, m); + break; + } + default: return; + } + break; + } + case 1: { + switch (msg_getType(m,0)) { + case HV_MSG_FLOAT: { + o->e.type = HV_MSG_FLOAT; + o->e.data.f = msg_getFloat(m,0); + break; + } + case HV_MSG_SYMBOL: + case HV_MSG_HASH: { + o->e.type = HV_MSG_HASH; + o->e.data.h = msg_getHash(m,0); + break; + } + default: break; + } + } + default: return; + } +} diff --git a/delay_simple/c/HvControlVar.h b/delay_simple/c/HvControlVar.h new file mode 100644 index 0000000..bf39ade --- /dev/null +++ b/delay_simple/c/HvControlVar.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_VAR_H_ +#define _HEAVY_CONTROL_VAR_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ControlVar { + Element e; // type is only every HV_MSG_FLOAT or HV_MSG_HASH +} ControlVar; + +hv_size_t cVar_init_f(ControlVar *o, float k); + +hv_size_t cVar_init_s(ControlVar *o, const char *s); + +void cVar_free(ControlVar *o); + +void cVar_onMessage(HeavyContextInterface *_c, ControlVar *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_VAR_H_ diff --git a/delay_simple/c/HvHeavy.cpp b/delay_simple/c/HvHeavy.cpp new file mode 100644 index 0000000..9f70f6c --- /dev/null +++ b/delay_simple/c/HvHeavy.cpp @@ -0,0 +1,318 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HeavyContext.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !HV_WIN +#pragma mark - Heavy Table +#endif + +HV_EXPORT bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength) { + hv_assert(c != nullptr); + return c->setLengthForTable(tableHash, newSampleLength); +} + +HV_EXPORT float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash) { + hv_assert(c != nullptr); + return c->getBufferForTable(tableHash); +} + +HV_EXPORT hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash) { + hv_assert(c != nullptr); + return c->getLengthForTable(tableHash); +} + + + +#if !HV_WIN +#pragma mark - Heavy Message +#endif + +HV_EXPORT hv_size_t hv_msg_getByteSize(hv_uint32_t numElements) { + return msg_getCoreSize(numElements); +} + +HV_EXPORT void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp) { + msg_init(m, numElements, timestamp); +} + +HV_EXPORT hv_size_t hv_msg_getNumElements(const HvMessage *m) { + return msg_getNumElements(m); +} + +HV_EXPORT hv_uint32_t hv_msg_getTimestamp(const HvMessage *m) { + return msg_getTimestamp(m); +} + +HV_EXPORT void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { + msg_setTimestamp(m, timestamp); +} + +HV_EXPORT bool hv_msg_isBang(const HvMessage *const m, int i) { + return msg_isBang(m,i); +} + +HV_EXPORT void hv_msg_setBang(HvMessage *m, int i) { + msg_setBang(m,i); +} + +HV_EXPORT bool hv_msg_isFloat(const HvMessage *const m, int i) { + return msg_isFloat(m, i); +} + +HV_EXPORT float hv_msg_getFloat(const HvMessage *const m, int i) { + return msg_getFloat(m,i); +} + +HV_EXPORT void hv_msg_setFloat(HvMessage *m, int i, float f) { + msg_setFloat(m,i,f); +} + +HV_EXPORT bool hv_msg_isSymbol(const HvMessage *const m, int i) { + return msg_isSymbol(m,i); +} + +HV_EXPORT const char *hv_msg_getSymbol(const HvMessage *const m, int i) { + return msg_getSymbol(m,i); +} + +HV_EXPORT void hv_msg_setSymbol(HvMessage *m, int i, const char *s) { + msg_setSymbol(m,i,s); +} + +HV_EXPORT bool hv_msg_isHash(const HvMessage *const m, int i) { + return msg_isHash(m, i); +} + +HV_EXPORT hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i) { + return msg_getHash(m, i); +} + +HV_EXPORT bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt) { + return msg_hasFormat(m, fmt); +} + +HV_EXPORT char *hv_msg_toString(const HvMessage *const m) { + return msg_toString(m); +} + +HV_EXPORT HvMessage *hv_msg_copy(const HvMessage *const m) { + return msg_copy(m); +} + +HV_EXPORT void hv_msg_free(HvMessage *m) { + msg_free(m); +} + + + +#if !HV_WIN +#pragma mark - Heavy Common +#endif + +HV_EXPORT int hv_getSize(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return (int) c->getSize(); +} + +HV_EXPORT double hv_getSampleRate(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getSampleRate(); +} + +HV_EXPORT int hv_getNumInputChannels(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getNumInputChannels(); +} + +HV_EXPORT int hv_getNumOutputChannels(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getNumOutputChannels(); +} + +HV_EXPORT void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f) { + hv_assert(c != nullptr); + c->setPrintHook(f); +} + +HV_EXPORT HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getPrintHook(); +} + +HV_EXPORT void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f) { + hv_assert(c != nullptr); + c->setSendHook(f); +} + +HV_EXPORT hv_uint32_t hv_stringToHash(const char *s) { + return hv_string_to_hash(s); +} + +HV_EXPORT bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash) { + hv_assert(c != nullptr); + return c->sendBangToReceiver(receiverHash); +} + +HV_EXPORT bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, float x) { + hv_assert(c != nullptr); + return c->sendFloatToReceiver(receiverHash, x); +} + +HV_EXPORT bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s) { + hv_assert(c != nullptr); + return c->sendSymbolToReceiver(receiverHash, s); +} + +HV_EXPORT bool hv_sendMessageToReceiverV( + HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...) { + hv_assert(c != nullptr); + hv_assert(delayMs >= 0.0); + hv_assert(format != nullptr); + + va_list ap; + va_start(ap, format); + const int numElem = (int) hv_strlen(format); + HvMessage *m = HV_MESSAGE_ON_STACK(numElem); + msg_init(m, numElem, c->getCurrentSample() + (hv_uint32_t) (hv_max_d(0.0, delayMs)*c->getSampleRate()/1000.0)); + for (int i = 0; i < numElem; i++) { + switch (format[i]) { + case 'b': msg_setBang(m, i); break; + case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; + case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; + case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; + default: break; + } + } + va_end(ap); + + return c->sendMessageToReceiver(receiverHash, delayMs, m); +} + +HV_EXPORT bool hv_sendMessageToReceiver( + HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m) { + hv_assert(c != nullptr); + return c->sendMessageToReceiver(receiverHash, delayMs, m); +} + +HV_EXPORT void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + hv_assert(c != nullptr); + c->cancelMessage(m, sendMessage); +} + +HV_EXPORT const char *hv_getName(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getName(); +} + +HV_EXPORT void hv_setUserData(HeavyContextInterface *c, void *userData) { + hv_assert(c != nullptr); + c->setUserData(userData); +} + +HV_EXPORT void *hv_getUserData(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getUserData(); +} + +HV_EXPORT double hv_getCurrentTime(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return (double) c->samplesToMilliseconds(c->getCurrentSample()); +} + +HV_EXPORT hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getCurrentSample(); +} + +HV_EXPORT float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples) { + hv_assert(c != nullptr); + return c->samplesToMilliseconds(numSamples); +} + +HV_EXPORT hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms) { + hv_assert(c != nullptr); + return c->millisecondsToSamples(ms); +} + +HV_EXPORT int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info) { + hv_assert(c != nullptr); + return c->getParameterInfo(index, info); +} + +HV_EXPORT void hv_lock_acquire(HeavyContextInterface *c) { + hv_assert(c != nullptr); + c->lockAcquire(); +} + +HV_EXPORT bool hv_lock_try(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->lockTry(); +} + +HV_EXPORT void hv_lock_release(HeavyContextInterface *c) { + hv_assert(c != nullptr); + c->lockRelease(); +} + +HV_EXPORT void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb) { + hv_assert(c != nullptr); + c->setInputMessageQueueSize(inQueueKb); +} + +HV_EXPORT void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb) { + hv_assert(c != nullptr); + c->setOutputMessageQueueSize(outQueueKb); +} + +HV_EXPORT bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength) { + hv_assert(c != nullptr); + hv_assert(destinationHash != nullptr); + hv_assert(outMsg != nullptr); + return c->getNextSentMessage(destinationHash, outMsg, msgLength); +} + + +#if !HV_WIN +#pragma mark - Heavy Common +#endif + +HV_EXPORT int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n) { + hv_assert(c != nullptr); + return c->process(inputBuffers, outputBuffers, n); +} + +HV_EXPORT int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { + hv_assert(c != nullptr); + return c->processInline(inputBuffers, outputBuffers, n); +} + +HV_EXPORT int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { + hv_assert(c != nullptr); + return c->processInlineInterleaved(inputBuffers, outputBuffers, n); +} + +HV_EXPORT void hv_delete(HeavyContextInterface *c) { + delete c; +} + +#ifdef __cplusplus +} +#endif diff --git a/delay_simple/c/HvHeavy.h b/delay_simple/c/HvHeavy.h new file mode 100644 index 0000000..cb1aecf --- /dev/null +++ b/delay_simple/c/HvHeavy.h @@ -0,0 +1,413 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_H_ +#define _HEAVY_H_ + +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _HEAVY_DECLARATIONS_ +#define _HEAVY_DECLARATIONS_ + +#ifdef __cplusplus +class HeavyContextInterface; +#else +typedef struct HeavyContextInterface HeavyContextInterface; +#endif + +typedef struct HvMessage HvMessage; + +typedef enum { + HV_PARAM_TYPE_PARAMETER_IN, + HV_PARAM_TYPE_PARAMETER_OUT, + HV_PARAM_TYPE_EVENT_IN, + HV_PARAM_TYPE_EVENT_OUT +} HvParameterType; + +typedef struct HvParameterInfo { + const char *name; // the human readable parameter name + hv_uint32_t hash; // an integer identified used by heavy for this parameter + HvParameterType type; // type of this parameter + float minVal; // the minimum value of this parameter + float maxVal; // the maximum value of this parameter + float defaultVal; // the default value of this parameter +} HvParameterInfo; + +typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); +typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); + +#endif // _HEAVY_DECLARATIONS_ + + + +#if HV_APPLE +#pragma mark - Heavy Context +#endif + +/** Deletes a patch instance. */ +void hv_delete(HeavyContextInterface *c); + + + +#if HV_APPLE +#pragma mark - Heavy Process +#endif + +/** + * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [[LLLL][RRRR]] + * This function support in-place processing. + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ +int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n); + +/** + * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LLLLRRRR] + * This function support in-place processing. + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ +int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); + +/** + * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LRLRLRLR] + * This function support in-place processing. + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ +int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); + + + +#if HV_APPLE +#pragma mark - Heavy Common +#endif + +/** + * Returns the total size in bytes of the context. + * This value may change if tables are resized. + */ +int hv_getSize(HeavyContextInterface *c); + +/** Returns the sample rate with which this context has been configured. */ +double hv_getSampleRate(HeavyContextInterface *c); + +/** Returns the number of input channels with which this context has been configured. */ +int hv_getNumInputChannels(HeavyContextInterface *c); + +/** Returns the number of output channels with which this context has been configured. */ +int hv_getNumOutputChannels(HeavyContextInterface *c); + +/** Set the print hook. The function is called whenever a message is sent to a print object. */ +void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f); + +/** Returns the print hook, or NULL. */ +HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c); + +/** + * Set the send hook. The function is called whenever a message is sent to any send object. + * Messages returned by this function should NEVER be freed. If the message must persist, call + * hv_msg_copy() first. + */ +void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f); + +/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ +hv_uint32_t hv_stringToHash(const char *s); + +/** + * A convenience function to send a bang to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash); + +/** + * A convenience function to send a float to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, const float x); + +/** + * A convenience function to send a symbol to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s); + +/** + * Sends a formatted message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendMessageToReceiverV(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...); + +/** + * Sends a message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendMessageToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m); + +/** + * Cancels a previously scheduled message. + * + * @param sendMessage May be NULL. + */ +void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Returns the read-only user-assigned name of this patch. */ +const char *hv_getName(HeavyContextInterface *c); + +/** Sets a user-definable value. This value is never manipulated by Heavy. */ +void hv_setUserData(HeavyContextInterface *c, void *userData); + +/** Returns the user-defined data. */ +void *hv_getUserData(HeavyContextInterface *c); + +/** Returns the current patch time in milliseconds. This value may have rounding errors. */ +double hv_getCurrentTime(HeavyContextInterface *c); + +/** Returns the current patch time in samples. This value is always exact. */ +hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c); + +/** + * Returns information about each parameter such as name, hash, and range. + * The total number of parameters is always returned. + * + * @param index The parameter index. + * @param info A pointer to a HvParameterInfo struct. May be null. + * + * @return The total number of parameters. + */ +int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info); + +/** */ +float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples); + +/** Converts milliseconds to samples. Input is limited to non-negative range. */ +hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms); + +/** + * Acquire the input message queue lock. + * + * This function will block until the message lock as been acquired. + * Typical applications will not require the use of this function. + * + * @param c A Heavy context. + */ +void hv_lock_acquire(HeavyContextInterface *c); + +/** + * Try to acquire the input message queue lock. + * + * If the lock has been acquired, hv_lock_release() must be called to release it. + * Typical applications will not require the use of this function. + * + * @param c A Heavy context. + * + * @return Returns true if the lock has been acquired, false otherwise. + */ +bool hv_lock_try(HeavyContextInterface *c); + +/** + * Release the input message queue lock. + * + * Typical applications will not require the use of this function. + * + * @param c A Heavy context. + */ +void hv_lock_release(HeavyContextInterface *c); + +/** + * Set the size of the input message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * + * @param c A Heavy context. + * @param inQueueKb Must be positive i.e. at least one. + */ +void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb); + +/** + * Set the size of the output message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * Only the default sendhook uses the outgoing message queue. If the default + * sendhook is not being used, then this function is not useful. + * + * @param c A Heavy context. + * @param outQueueKb Must be postive i.e. at least one. + */ +void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb); + +/** + * Get the next message in the outgoing queue, will also consume the message. + * Returns false if there are no messages. + * + * @param c A Heavy context. + * @param destinationHash a hash of the name of the receiver the message was sent to. + * @param outMsg message pointer that is filled by the next message contents. + * @param msgLength length of outMsg in bytes. + * + * @return True if there is a message in the outgoing queue. +*/ +bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength); + + + +#if HV_APPLE +#pragma mark - Heavy Message +#endif + +typedef struct HvMessage HvMessage; + +/** Returns the total size in bytes of a HvMessage with a number of elements on the heap. */ +unsigned long hv_msg_getByteSize(hv_uint32_t numElements); + +/** Initialise a HvMessage structure with the number of elements and a timestamp (in samples). */ +void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp); + +/** Returns the number of elements in this message. */ +unsigned long hv_msg_getNumElements(const HvMessage *m); + +/** Returns the time at which this message exists (in samples). */ +hv_uint32_t hv_msg_getTimestamp(const HvMessage *m); + +/** Set the time at which this message should be executed (in samples). */ +void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp); + +/** Returns true of the indexed element is a bang. False otherwise. Index is not bounds checked. */ +bool hv_msg_isBang(const HvMessage *const m, int i); + +/** Sets the indexed element to a bang. Index is not bounds checked. */ +void hv_msg_setBang(HvMessage *m, int i); + +/** Returns true of the indexed element is a float. False otherwise. Index is not bounds checked. */ +bool hv_msg_isFloat(const HvMessage *const m, int i); + +/** Returns the indexed element as a float value. Index is not bounds checked. */ +float hv_msg_getFloat(const HvMessage *const m, int i); + +/** Sets the indexed element to float value. Index is not bounds checked. */ +void hv_msg_setFloat(HvMessage *m, int i, float f); + +/** Returns true of the indexed element is a symbol. False otherwise. Index is not bounds checked. */ +bool hv_msg_isSymbol(const HvMessage *const m, int i); + +/** Returns the indexed element as a symbol value. Index is not bounds checked. */ +const char *hv_msg_getSymbol(const HvMessage *const m, int i); + +/** Returns true of the indexed element is a hash. False otherwise. Index is not bounds checked. */ +bool hv_msg_isHash(const HvMessage *const m, int i); + +/** Returns the indexed element as a hash value. Index is not bounds checked. */ +hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i); + +/** Sets the indexed element to symbol value. Index is not bounds checked. */ +void hv_msg_setSymbol(HvMessage *m, int i, const char *s); + +/** + * Returns true if the message has the given format, in number of elements and type. False otherwise. + * Valid element types are: + * 'b': bang + * 'f': float + * 's': symbol + * + * For example, a message with three floats would have a format of "fff". A single bang is "b". + * A message with two symbols is "ss". These types can be mixed and matched in any way. + */ +bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt); + +/** + * Returns a basic string representation of the message. + * The character array MUST be deallocated by the caller. + */ +char *hv_msg_toString(const HvMessage *const m); + +/** Copy a message onto the stack. The message persists. */ +HvMessage *hv_msg_copy(const HvMessage *const m); + +/** Free a copied message. */ +void hv_msg_free(HvMessage *m); + + + +#if HV_APPLE +#pragma mark - Heavy Table +#endif + +/** + * Resizes the table to the given length. + * + * Existing contents are copied to the new table. Remaining space is cleared + * if the table is longer than the original, truncated otherwise. + * + * @param tableHash The table identifier. + * @param newSampleLength The new length of the table, in samples. Must be positive. + * + * @return False if the table could not be found. True otherwise. + */ +bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength); + +/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ +float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash); + +/** Returns the length of this table in samples. */ +hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_H_ diff --git a/delay_simple/c/HvHeavyInternal.h b/delay_simple/c/HvHeavyInternal.h new file mode 100644 index 0000000..e10b944 --- /dev/null +++ b/delay_simple/c/HvHeavyInternal.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_INTERNAL_H_ +#define _HEAVY_INTERNAL_H_ + +#include "HvHeavy.h" +#include "HvUtils.h" +#include "HvTable.h" +#include "HvMessage.h" +#include "HvMath.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + */ +HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash); + +/** + * + */ +void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m); + +/** + * + */ +HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/delay_simple/c/HvLightPipe.c b/delay_simple/c/HvLightPipe.c new file mode 100644 index 0000000..98de428 --- /dev/null +++ b/delay_simple/c/HvLightPipe.c @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvLightPipe.h" + +#if __SSE__ || HV_SIMD_SSE +#include +#define hv_sfence() _mm_sfence() +#elif __arm__ || HV_SIMD_NEON + #if __ARM_ACLE + #include + // https://msdn.microsoft.com/en-us/library/hh875058.aspx#BarrierRestrictions + // http://doxygen.reactos.org/d8/d47/armintr_8h_a02be7ec76ca51842bc90d9b466b54752.html + #define hv_sfence() __dmb(0xE) /* _ARM_BARRIER_ST */ + #elif defined(__GNUC__) + #define hv_sfence() __asm__ volatile ("dmb 0xE":::"memory") + #else + // http://stackoverflow.com/questions/19965076/gcc-memory-barrier-sync-synchronize-vs-asm-volatile-memory + #define hv_sfence() __sync_synchronize() + #endif +#elif HV_WIN +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx +#define hv_sfence() _WriteBarrier() +#else +#define hv_sfence() __asm__ volatile("" : : : "memory") +#endif + +#define HLP_STOP 0 +#define HLP_LOOP 0xFFFFFFFF +#define HLP_SET_UINT32_AT_BUFFER(a, b) (*((hv_uint32_t *) (a)) = (b)) +#define HLP_GET_UINT32_AT_BUFFER(a) (*((hv_uint32_t *) (a))) + +hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes) { + if (numBytes > 0) { + q->buffer = (char *) hv_malloc(numBytes); + hv_assert(q->buffer != NULL); + HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); + } else { + q->buffer = NULL; + } + q->writeHead = q->buffer; + q->readHead = q->buffer; + q->len = numBytes; + q->remainingBytes = numBytes; + return numBytes; +} + +void hLp_free(HvLightPipe *q) { + hv_free(q->buffer); +} + +hv_uint32_t hLp_hasData(HvLightPipe *q) { + hv_uint32_t x = HLP_GET_UINT32_AT_BUFFER(q->readHead); + if (x == HLP_LOOP) { + q->readHead = q->buffer; + x = HLP_GET_UINT32_AT_BUFFER(q->readHead); + } + return x; +} + +char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t bytesToWrite) { + char *const readHead = q->readHead; + char *const oldWriteHead = q->writeHead; + const hv_uint32_t totalByteRequirement = bytesToWrite + 2*sizeof(hv_uint32_t); + + // check if there is enough space to write the data in the remaining + // length of the buffer + if (totalByteRequirement <= q->remainingBytes) { + char *const newWriteHead = oldWriteHead + sizeof(hv_uint32_t) + bytesToWrite; + + // check if writing would overwrite existing data in the pipe (return NULL if so) + if ((oldWriteHead < readHead) && (newWriteHead >= readHead)) return NULL; + else return (oldWriteHead + sizeof(hv_uint32_t)); + } else { + // there isn't enough space, try looping around to the start + if (totalByteRequirement <= q->len) { + if ((oldWriteHead < readHead) || ((q->buffer + totalByteRequirement) > readHead)) { + return NULL; // overwrite condition + } else { + q->writeHead = q->buffer; + q->remainingBytes = q->len; + HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); + hv_sfence(); + HLP_SET_UINT32_AT_BUFFER(oldWriteHead, HLP_LOOP); + return q->buffer + sizeof(hv_uint32_t); + } + } else { + return NULL; // there isn't enough space to write the data + } + } +} + +void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes) { + hv_assert(q->remainingBytes >= (numBytes + 2*sizeof(hv_uint32_t))); + q->remainingBytes -= (sizeof(hv_uint32_t) + numBytes); + char *const oldWriteHead = q->writeHead; + q->writeHead += (sizeof(hv_uint32_t) + numBytes); + HLP_SET_UINT32_AT_BUFFER(q->writeHead, HLP_STOP); + + // save everything before this point to memory + hv_sfence(); + + // then save this + HLP_SET_UINT32_AT_BUFFER(oldWriteHead, numBytes); +} + +char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes) { + *numBytes = HLP_GET_UINT32_AT_BUFFER(q->readHead); + char *const readBuffer = q->readHead + sizeof(hv_uint32_t); + return readBuffer; +} + +void hLp_consume(HvLightPipe *q) { + hv_assert(HLP_GET_UINT32_AT_BUFFER(q->readHead) != HLP_STOP); + q->readHead += sizeof(hv_uint32_t) + HLP_GET_UINT32_AT_BUFFER(q->readHead); +} + +void hLp_reset(HvLightPipe *q) { + q->writeHead = q->buffer; + q->readHead = q->buffer; + q->remainingBytes = q->len; + memset(q->buffer, 0, q->len); +} diff --git a/delay_simple/c/HvLightPipe.h b/delay_simple/c/HvLightPipe.h new file mode 100644 index 0000000..438f726 --- /dev/null +++ b/delay_simple/c/HvLightPipe.h @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_LIGHTPIPE_H_ +#define _HEAVY_LIGHTPIPE_H_ + +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This pipe assumes that there is only one producer thread and one consumer + * thread. This data structure does not support any other configuration. + */ +typedef struct HvLightPipe { + char *buffer; + char *writeHead; + char *readHead; + hv_uint32_t len; + hv_uint32_t remainingBytes; // total bytes from write head to end +} HvLightPipe; + +/** + * Initialise the pipe with a given length, in bytes. + * @return Returns the size of the pipe in bytes. + */ +hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes); + +/** + * Frees the internal buffer. + * @param q The light pipe. + */ +void hLp_free(HvLightPipe *q); + +/** + * Indicates if data is available for reading. + * @param q The light pipe. + * + * @return Returns the number of bytes available for reading. Zero if no bytes + * are available. + */ +hv_uint32_t hLp_hasData(HvLightPipe *q); + +/** + * Returns a pointer to a location in the pipe where numBytes can be written. + * + * @param numBytes The number of bytes to be written. + * @return A pointer to a location where those bytes can be written. Returns + * NULL if no more space is available. Successive calls to this + * function may eventually return a valid pointer because the readhead + * has been advanced on another thread. + */ +char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t numBytes); + +/** + * Indicates to the pipe how many bytes have been written. + * + * @param numBytes The number of bytes written. In general this should be the + * same value as was passed to the preceeding call to + * hLp_getWriteBuffer(). + */ +void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes); + +/** + * Returns the current read buffer, indicating the number of bytes available + * for reading. + * @param q The light pipe. + * @param numBytes This value will be filled with the number of bytes available + * for reading. + * + * @return A pointer to the read buffer. + */ +char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes); + +/** + * Indicates that the next set of bytes have been read and are no longer needed. + * @param q The light pipe. + */ +void hLp_consume(HvLightPipe *q); + +// resets the queue to it's initialised state +// This should be done when only one thread is accessing the pipe. +void hLp_reset(HvLightPipe *q); + +#ifdef __cplusplus +} +#endif + +#endif // _HEAVY_LIGHTPIPE_H_ diff --git a/delay_simple/c/HvMath.h b/delay_simple/c/HvMath.h new file mode 100644 index 0000000..8bdcd6b --- /dev/null +++ b/delay_simple/c/HvMath.h @@ -0,0 +1,724 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_MATH_H_ +#define _HEAVY_MATH_H_ + +#include "HvUtils.h" + +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/ +// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/ARM-NEON-Intrinsics.html +// http://codesuppository.blogspot.co.uk/2015/02/sse2neonh-porting-guide-and-header-file.html + +static inline void __hv_zero_f(hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_setzero_ps(); +#elif HV_SIMD_SSE + *bOut = _mm_setzero_ps(); +#elif HV_SIMD_NEON + *bOut = vdupq_n_f32(0.0f); +#else // HV_SIMD_NONE + *bOut = 0.0f; +#endif +} + +static inline void __hv_zero_i(hv_bOuti_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_setzero_si256(); +#elif HV_SIMD_SSE + *bOut = _mm_setzero_si128(); +#elif HV_SIMD_NEON + *bOut = vdupq_n_s32(0); +#else // HV_SIMD_NONE + *bOut = 0; +#endif +} + +static inline void __hv_load_f(float *bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_load_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_load_ps(bIn); +#elif HV_SIMD_NEON + *bOut = vld1q_f32(bIn); +#else // HV_SIMD_NONE + *bOut = *bIn; +#endif +} + +static inline void __hv_store_f(float *bOut, hv_bInf_t bIn) { +#if HV_SIMD_AVX + _mm256_store_ps(bOut, bIn); +#elif HV_SIMD_SSE + _mm_store_ps(bOut, bIn); +#elif HV_SIMD_NEON + vst1q_f32(bOut, bIn); +#else // HV_SIMD_NONE + *bOut = bIn; +#endif +} + +static inline void __hv_log2_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_log2_f() not implemented +#elif HV_SIMD_SSE + // https://en.wikipedia.org/wiki/Fast_inverse_square_root + __m128i a = _mm_castps_si128(bIn); + __m128i b = _mm_srli_epi32(a, 23); + __m128i c = _mm_sub_epi32(b, _mm_set1_epi32(127)); // exponent (int) + __m128 d = _mm_cvtepi32_ps(c); // exponent (float) + __m128i e = _mm_or_si128(_mm_andnot_si128(_mm_set1_epi32(0xFF800000), a), _mm_set1_epi32(0x3F800000)); + __m128 f = _mm_castsi128_ps(e); // 1+m (float) + __m128 g = _mm_add_ps(d, f); // e + 1 + m + __m128 h = _mm_add_ps(g, _mm_set1_ps(-0.9569643f)); // e + 1 + m + (sigma-1) + *bOut = h; +#elif HV_SIMD_NEON + int32x4_t a = vreinterpretq_s32_f32(bIn); + int32x4_t b = vshrq_n_s32(a, 23); + int32x4_t c = vsubq_s32(b, vdupq_n_s32(127)); + float32x4_t d = vcvtq_f32_s32(c); + int32x4_t e = vorrq_s32(vbicq_s32(a, vdupq_n_s32(0xFF800000)), vdupq_n_s32(0x3F800000)); + float32x4_t f = vreinterpretq_f32_s32(e); + float32x4_t g = vaddq_f32(d, f); + float32x4_t h = vaddq_f32(g, vdupq_n_f32(-0.9569643f)); + *bOut = h; +#else // HV_SIMD_NONE + *bOut = 1.442695040888963f * hv_log_f(bIn); +#endif +} + +// NOTE(mhroth): this is a pretty ghetto implementation +static inline void __hv_cos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_set_ps( + hv_cos_f(bIn[7]), hv_cos_f(bIn[6]), hv_cos_f(bIn[5]), hv_cos_f(bIn[4]), + hv_cos_f(bIn[3]), hv_cos_f(bIn[2]), hv_cos_f(bIn[1]), hv_cos_f(bIn[0])); +#elif HV_SIMD_SSE + const float *const b = (float *) &bIn; + *bOut = _mm_set_ps(hv_cos_f(b[3]), hv_cos_f(b[2]), hv_cos_f(b[1]), hv_cos_f(b[0])); +#elif HV_SIMD_NEON + *bOut = (float32x4_t) {hv_cos_f(bIn[0]), hv_cos_f(bIn[1]), hv_cos_f(bIn[2]), hv_cos_f(bIn[3])}; +#else // HV_SIMD_NONE + *bOut = hv_cos_f(bIn); +#endif +} + +static inline void __hv_acos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_acos_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_acos_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_acos_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_acos_f(bIn); +#endif +} + +static inline void __hv_cosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_cosh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_cosh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_cosh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_cosh_f(bIn); +#endif +} + +static inline void __hv_acosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_acosh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_acosh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_acosh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_acosh_f(bIn); +#endif +} + +static inline void __hv_sin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_sin_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_sin_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_sin_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_sin_f(bIn); +#endif +} + +static inline void __hv_asin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_asin_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_asin_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_asin_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_asin_f(bIn); +#endif +} + +static inline void __hv_sinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_sinh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_sinh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_sinh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_sinh_f(bIn); +#endif +} + +static inline void __hv_asinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_asinh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_asinh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_asinh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_asinh_f(bIn); +#endif +} + +static inline void __hv_tan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_tan_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_tan_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_tan_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_tan_f(bIn); +#endif +} + +static inline void __hv_atan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_atan_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_atan_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_atan_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_atan_f(bIn); +#endif +} + +static inline void __hv_atan2_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_atan2_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_atan2_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_atan2_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_atan2_f(bIn0, bIn1); +#endif +} + +static inline void __hv_tanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_tanh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_tanh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_tanh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_tanh_f(bIn); +#endif +} + +static inline void __hv_atanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_atanh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_atanh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_atanh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_atanh_f(bIn); +#endif +} + +static inline void __hv_sqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_sqrt_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_sqrt_ps(bIn); +#elif HV_SIMD_NEON + const float32x4_t y = vrsqrteq_f32(bIn); + *bOut = vmulq_f32(bIn, vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y)); // numerical results may be inexact +#else // HV_SIMD_NONE + *bOut = hv_sqrt_f(bIn); +#endif +} + +static inline void __hv_rsqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_rsqrt_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_rsqrt_ps(bIn); +#elif HV_SIMD_NEON + const float32x4_t y = vrsqrteq_f32(bIn); + *bOut = vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y); // numerical results may be inexact +#else // HV_SIMD_NONE + *bOut = 1.0f/hv_sqrt_f(bIn); +#endif +} + +static inline void __hv_abs_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), bIn); +#elif HV_SIMD_SSE + *bOut = _mm_andnot_ps(_mm_set1_ps(-0.0f), bIn); // == 1 << 31 +#elif HV_SIMD_NEON + *bOut = vabsq_f32(bIn); +#else // HV_SIMD_NONE + *bOut = hv_abs_f(bIn); +#endif +} + +static inline void __hv_neg_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_xor_ps(bIn, _mm256_set1_ps(-0.0f)); +#elif HV_SIMD_SSE + *bOut = _mm_xor_ps(bIn, _mm_set1_ps(-0.0f)); +#elif HV_SIMD_NEON + *bOut = vnegq_f32(bIn); +#else // HV_SIMD_NONE + *bOut = bIn * -1.0f; +#endif +} + +static inline void __hv_exp_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); + _mm256_store_ps(b, bIn); + *bOut = _mm256_set_ps( + hv_exp_f(b[7]), hv_exp_f(b[6]), hv_exp_f(b[5]), hv_exp_f(b[4]), + hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); +#elif HV_SIMD_SSE + float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); + _mm_store_ps(b, bIn); + *bOut = _mm_set_ps(hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); +#elif HV_SIMD_NEON + *bOut = (float32x4_t) { + hv_exp_f(bIn[0]), + hv_exp_f(bIn[1]), + hv_exp_f(bIn[2]), + hv_exp_f(bIn[3])}; +#else // HV_SIMD_NONE + *bOut = hv_exp_f(bIn); +#endif +} + +static inline void __hv_ceil_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_ceil_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_ceil_ps(bIn); +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vrndpq_f32(bIn); +#else + // A slow NEON implementation of __hv_ceil_f() is being used because + // the necessary intrinsic cannot be found. It is only available in ARMv8. + *bOut = (float32x4_t) {hv_ceil_f(bIn[0]), hv_ceil_f(bIn[1]), hv_ceil_f(bIn[2]), hv_ceil_f(bIn[3])}; +#endif // vrndpq_f32 +#else // HV_SIMD_NONE + *bOut = hv_ceil_f(bIn); +#endif +} + +static inline void __hv_floor_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_floor_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_floor_ps(bIn); +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vrndmq_f32(bIn); +#else + // A slow implementation of __hv_floor_f() is being used because + // the necessary intrinsic cannot be found. It is only available from ARMv8. + *bOut = (float32x4_t) {hv_floor_f(bIn[0]), hv_floor_f(bIn[1]), hv_floor_f(bIn[2]), hv_floor_f(bIn[3])}; +#endif // vrndmq_f32 +#else // HV_SIMD_NONE + *bOut = hv_floor_f(bIn); +#endif +} + +// __add~f +static inline void __hv_add_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_add_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_add_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vaddq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 + bIn1; +#endif +} + +// __add~i +static inline void __hv_add_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_add_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_add_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_add_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vaddq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 + bIn1; +#endif +} + +// __sub~f +static inline void __hv_sub_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_sub_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_sub_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vsubq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 - bIn1; +#endif +} + +// __mul~f +static inline void __hv_mul_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_mul_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_mul_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmulq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 * bIn1; +#endif +} + +// __*~i +static inline void __hv_mul_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_mullo_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_mullo_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_mullo_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmulq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 * bIn1; +#endif +} + +// __cast~if +static inline void __hv_cast_if(hv_bIni_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cvtepi32_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_cvtepi32_ps(bIn); +#elif HV_SIMD_NEON + *bOut = vcvtq_f32_s32(bIn); +#else // HV_SIMD_NONE + *bOut = (float) bIn; +#endif +} + +// __cast~fi +static inline void __hv_cast_fi(hv_bInf_t bIn, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cvtps_epi32(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_cvtps_epi32(bIn); +#elif HV_SIMD_NEON + *bOut = vcvtq_s32_f32(bIn); +#else // HV_SIMD_NONE + *bOut = (int) bIn; +#endif +} + +static inline void __hv_div_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + __m256 a = _mm256_cmp_ps(bIn1, _mm256_setzero_ps(), _CMP_EQ_OQ); + __m256 b = _mm256_div_ps(bIn0, bIn1); + *bOut = _mm256_andnot_ps(a, b); +#elif HV_SIMD_SSE + __m128 a = _mm_cmpeq_ps(bIn1, _mm_setzero_ps()); + __m128 b = _mm_div_ps(bIn0, bIn1); + *bOut = _mm_andnot_ps(a, b); +#elif HV_SIMD_NEON + uint32x4_t a = vceqq_f32(bIn1, vdupq_n_f32(0.0f)); + float32x4_t b = vmulq_f32(bIn0, vrecpeq_f32(bIn1)); // NOTE(mhroth): numerical results may be inexact + *bOut = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(b), a)); +#else // HV_SIMD_NONE + *bOut = (bIn1 != 0.0f) ? (bIn0 / bIn1) : 0.0f; +#endif +} + +static inline void __hv_min_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_min_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_min_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vminq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_min_f(bIn0, bIn1); +#endif +} + +static inline void __hv_min_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_min_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_min_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_min_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vminq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_min_i(bIn0, bIn1); +#endif +} + +static inline void __hv_max_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_max_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_max_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmaxq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_max_f(bIn0, bIn1); +#endif +} + +static inline void __hv_max_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_max_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_max_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_max_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmaxq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_max_i(bIn0, bIn1); +#endif +} + +static inline void __hv_pow_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + float *b = (float *) hv_alloca(16*sizeof(float)); + _mm256_store_ps(b, bIn0); + _mm256_store_ps(b+8, bIn1); + *bOut = _mm256_set_ps( + hv_pow_f(b[7], b[7]), + hv_pow_f(b[6], b[6]), + hv_pow_f(b[5], b[5]), + hv_pow_f(b[4], b[4]), + hv_pow_f(b[3], b[3]), + hv_pow_f(b[2], b[2]), + hv_pow_f(b[1], b[1]), + hv_pow_f(b[0], b[0])); +#elif HV_SIMD_SSE + float *b = (float *) hv_alloca(8*sizeof(float)); + _mm_store_ps(b, bIn0); + _mm_store_ps(b+4, bIn1); + *bOut = _mm_set_ps( + hv_pow_f(b[3], b[7]), + hv_pow_f(b[2], b[6]), + hv_pow_f(b[1], b[5]), + hv_pow_f(b[0], b[4])); +#elif HV_SIMD_NEON + *bOut = (float32x4_t) { + hv_pow_f(bIn0[0], bIn1[0]), + hv_pow_f(bIn0[1], bIn1[1]), + hv_pow_f(bIn0[2], bIn1[2]), + hv_pow_f(bIn0[3], bIn1[3])}; +#else // HV_SIMD_NONE + *bOut = hv_pow_f(bIn0, bIn1); +#endif +} + +static inline void __hv_gt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GT_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmpgt_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcgtq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 > bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_gte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GE_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmpge_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcgeq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 >= bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_lt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LT_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmplt_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcltq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 < bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_lte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LE_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmple_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcleq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 <= bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_neq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_NEQ_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmpneq_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(bIn0, bIn1))); +#else // HV_SIMD_NONE + *bOut = (bIn0 != bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_or_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_or_ps(bIn1, bIn0); +#elif HV_SIMD_SSE + *bOut = _mm_or_ps(bIn1, bIn0); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); +#else // HV_SIMD_NONE + if (bIn0 == 0.0f && bIn1 == 0.0f) *bOut = 0.0f; + else if (bIn0 == 0.0f) *bOut = bIn1; + else if (bIn1 == 0.0f) *bOut = bIn0; + else hv_assert(0); +#endif +} + +static inline void __hv_and_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_and_ps(bIn1, bIn0); +#elif HV_SIMD_SSE + *bOut = _mm_and_ps(bIn1, bIn0); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); +#else // HV_SIMD_NONE + if (bIn0 == 0.0f || bIn1 == 0.0f) *bOut = 0.0f; + else if (bIn0 == 1.0f) *bOut = bIn1; + else if (bIn1 == 1.0f) *bOut = bIn0; + else hv_assert(0); +#endif +} + +static inline void __hv_andnot_f(hv_bInf_t bIn0_mask, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_andnot_ps(bIn0_mask, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_andnot_ps(bIn0_mask, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(bIn1), vreinterpretq_s32_f32(bIn0_mask))); +#else // HV_SIMD_NONE + *bOut = (bIn0_mask == 0.0f) ? bIn1 : 0.0f; +#endif +} + +// bOut = (bIn0 * bIn1) + bIn2 +static inline void __hv_fma_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { +#if HV_SIMD_AVX +#if HV_SIMD_FMA + *bOut = _mm256_fmadd_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm256_add_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_SSE +#if HV_SIMD_FMA + *bOut = _mm_fmadd_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm_add_ps(_mm_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vfmaq_f32(bIn2, bIn0, bIn1); +#else + // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures + *bOut = vaddq_f32(vmulq_f32(bIn0, bIn1), bIn2); +#endif +#else // HV_SIMD_NONE + *bOut = hv_fma_f(bIn0, bIn1, bIn2); +#endif +} + +// bOut = (bIn0 * bIn1) - bIn2 +static inline void __hv_fms_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { +#if HV_SIMD_AVX +#if HV_SIMD_FMA + *bOut = _mm256_fmsub_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm256_sub_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_SSE +#if HV_SIMD_FMA + *bOut = _mm_fmsub_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm_sub_ps(_mm_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vfmsq_f32(bIn2, bIn0, bIn1); +#else + // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures + *bOut = vsubq_f32(vmulq_f32(bIn0, bIn1), bIn2); +#endif +#else // HV_SIMD_NONE + *bOut = (bIn0 * bIn1) - bIn2; +#endif +} + +#endif // _HEAVY_MATH_H_ diff --git a/delay_simple/c/HvMessage.c b/delay_simple/c/HvMessage.c new file mode 100644 index 0000000..0f1ac5d --- /dev/null +++ b/delay_simple/c/HvMessage.c @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvMessage.h" + +HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) { + m->timestamp = timestamp; + m->numElements = (hv_uint16_t) numElements; + m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements); + return m; +} + +HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage); + msg_setFloat(m, 0, f); + return m; +} + +HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage); + msg_setBang(m, 0); + return m; +} + +HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s); + msg_setSymbol(m, 0, s); + return m; +} + +HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage); + msg_setHash(m, 0, h); + return m; +} + +void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) { + HvMessage *r = (HvMessage *) buffer; + + hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m)); + + // assert that the message is not already larger than the length of the buffer + hv_assert(len_r <= len); + + // copy the basic message to the buffer + hv_memcpy(r, m, len_r); + + char *p = buffer + len_r; // points to the end of the base message + for (int i = 0; i < msg_getNumElements(m); ++i) { + if (msg_isSymbol(m,i)) { + const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char + hv_assert(len_r + symLen <= len); // stay safe! + hv_strncpy(p, msg_getSymbol(m,i), symLen); + msg_setSymbol(r, i, p); + p += symLen; + len_r += symLen; + } + } + + r->numBytes = (hv_uint16_t) len_r; // update the message size in memory +} + +// the message is serialised such that all symbol elements are placed in order at the end of the buffer +HvMessage *msg_copy(const HvMessage *m) { + const hv_uint32_t heapSize = msg_getSize(m); + char *r = (char *) hv_malloc(heapSize); + hv_assert(r != NULL); + msg_copyToBuffer(m, r, heapSize); + return (HvMessage *) r; +} + +void msg_free(HvMessage *m) { + hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message +} + +bool msg_hasFormat(const HvMessage *m, const char *fmt) { + hv_assert(fmt != NULL); + const int n = msg_getNumElements(m); + for (int i = 0; i < n; ++i) { + switch (fmt[i]) { + case 'b': if (!msg_isBang(m, i)) return false; break; + case 'f': if (!msg_isFloat(m, i)) return false; break; + case 'h': if (!msg_isHash(m, i)) return false; break; + case 's': if (!msg_isSymbol(m, i)) return false; break; + default: return false; + } + } + return (fmt[n] == '\0'); +} + +bool msg_compareSymbol(const HvMessage *m, int i, const char *s) { + switch (msg_getType(m,i)) { + case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s); + case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s)); + default: return false; + } +} + +bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) { + if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) { + if (msg_getType(m, i_m) == msg_getType(n, i_n)) { + switch (msg_getType(m, i_m)) { + case HV_MSG_BANG: return true; + case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n)); + case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n)); + case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n); + default: break; + } + } + } + return false; +} + +void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) { + switch (msg_getType(m, i_m)) { + case HV_MSG_BANG: msg_setBang(n, i_n); break; + case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break; + case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break; + case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m)); + default: break; + } +} + +hv_uint32_t msg_getHash(const HvMessage *const m, int i) { + hv_assert(i < msg_getNumElements(m)); // invalid index + switch (msg_getType(m,i)) { + case HV_MSG_BANG: return 0xFFFFFFFF; + case HV_MSG_FLOAT: { + float f = msg_getFloat(m,i); + return *((hv_uint32_t *) &f); + } + case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i)); + case HV_MSG_HASH: return (&(m->elem)+i)->data.h; + default: return 0; + } +} + +char *msg_toString(const HvMessage *m) { + hv_assert(msg_getNumElements(m) > 0); + int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int)); + int size = 0; // the total length of our final buffer + + // loop through every element in our list of atoms + // first loop figures out how long our buffer should be + for (int i = 0; i < msg_getNumElements(m); i++) { + // length of our string is each atom plus a space, or \0 on the end + switch (msg_getType(m, i)) { + case HV_MSG_BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break; + case HV_MSG_FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break; + case HV_MSG_SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break; + case HV_MSG_HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break; + default: break; + } + size += len[i]; + } + + hv_assert(size > 0); + + // now we do the piecewise concatenation into our final string + // the final buffer we will pass back after concatenating all strings - user should free it + char *finalString = (char *) hv_malloc(size*sizeof(char)); + hv_assert(finalString != NULL); + int pos = 0; + for (int i = 0; i < msg_getNumElements(m); i++) { + // put a string representation of each atom into the final string + switch (msg_getType(m, i)) { + case HV_MSG_BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break; + case HV_MSG_FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break; + case HV_MSG_SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break; + case HV_MSG_HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break; + default: break; + } + pos += len[i]; + finalString[pos-1] = 32; // ASCII space + } + finalString[size-1] = '\0'; // ensure that the string is null terminated + return finalString; +} diff --git a/delay_simple/c/HvMessage.h b/delay_simple/c/HvMessage.h new file mode 100644 index 0000000..a3fdd1c --- /dev/null +++ b/delay_simple/c/HvMessage.h @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_MESSAGE_H_ +#define _HEAVY_MESSAGE_H_ + +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum ElementType { + HV_MSG_BANG = 0, + HV_MSG_FLOAT = 1, + HV_MSG_SYMBOL = 2, + HV_MSG_HASH = 3 +} ElementType; + +typedef struct Element { + ElementType type; + union { + float f; // float + const char *s; // symbol + hv_uint32_t h; // hash + } data; +} Element; + +typedef struct HvMessage { + hv_uint32_t timestamp; // the sample at which this message should be processed + hv_uint16_t numElements; + hv_uint16_t numBytes; // the total number of bytes that this message occupies in memory, including strings + Element elem; +} HvMessage; + +typedef struct ReceiverMessagePair { + hv_uint32_t receiverHash; + HvMessage msg; +} ReceiverMessagePair; + +#define HV_MESSAGE_ON_STACK(_x) (HvMessage *) hv_alloca(msg_getCoreSize(_x)) + +/** Returns the number of bytes that this message consumes in memory, not including strings. */ +static inline hv_size_t msg_getCoreSize(hv_size_t numElements) { + hv_assert(numElements > 0); + return sizeof(HvMessage) + ((numElements-1) * sizeof(Element)); +} + +HvMessage *msg_copy(const HvMessage *m); + +/** Copies the message into the given buffer. The buffer must be at least as large as msg_getNumHeapBytes(). */ +void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len); + +void msg_setElementToFrom(HvMessage *n, int indexN, const HvMessage *const m, int indexM); + +/** Frees a message on the heap. Does nothing if argument is NULL. */ +void msg_free(HvMessage *m); + +HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp); + +HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f); + +HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp); + +HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s); + +HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h); + +static inline hv_uint32_t msg_getTimestamp(const HvMessage *m) { + return m->timestamp; +} + +static inline void msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { + m->timestamp = timestamp; +} + +static inline int msg_getNumElements(const HvMessage *m) { + return (int) m->numElements; +} + +/** Returns the total number of bytes this message consumes in memory. */ +static inline hv_uint32_t msg_getSize(const HvMessage *m) { + return m->numBytes; +} + +static inline ElementType msg_getType(const HvMessage *m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + return (&(m->elem)+index)->type; +} + +static inline void msg_setBang(HvMessage *m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + (&(m->elem)+index)->type = HV_MSG_BANG; + (&(m->elem)+index)->data.s = NULL; +} + +static inline bool msg_isBang(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_BANG) : false; +} + +static inline void msg_setFloat(HvMessage *m, int index, float f) { + hv_assert(index < msg_getNumElements(m)); // invalid index + (&(m->elem)+index)->type = HV_MSG_FLOAT; + (&(m->elem)+index)->data.f = f; +} + +static inline float msg_getFloat(const HvMessage *const m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + return (&(m->elem)+index)->data.f; +} + +static inline bool msg_isFloat(const HvMessage *const m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_FLOAT) : false; +} + +static inline void msg_setHash(HvMessage *m, int index, hv_uint32_t h) { + hv_assert(index < msg_getNumElements(m)); // invalid index + (&(m->elem)+index)->type = HV_MSG_HASH; + (&(m->elem)+index)->data.h = h; +} + +static inline bool msg_isHash(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_HASH) : false; +} + +/** Returns true if the element is a hash or symbol. False otherwise. */ +static inline bool msg_isHashLike(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? ((msg_getType(m, index) == HV_MSG_HASH) || (msg_getType(m, index) == HV_MSG_SYMBOL)) : false; +} + +/** Returns a 32-bit hash of the given element. */ +hv_uint32_t msg_getHash(const HvMessage *const m, int i); + +static inline void msg_setSymbol(HvMessage *m, int index, const char *s) { + hv_assert(index < msg_getNumElements(m)); // invalid index + hv_assert(s != NULL); + (&(m->elem)+index)->type = HV_MSG_SYMBOL; + (&(m->elem)+index)->data.s = s; + // NOTE(mhroth): if the same message container is reused and string reset, + // then the message size will be overcounted + m->numBytes += (hv_uint16_t) (hv_strlen(s) + 1); // also count '\0' +} + +static inline const char *msg_getSymbol(const HvMessage *m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + return (&(m->elem)+index)->data.s; +} + +static inline bool msg_isSymbol(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_SYMBOL) : false; +} + +bool msg_compareSymbol(const HvMessage *m, int i, const char *s); + +/** Returns 1 if the element i_m of message m is equal to element i_n of message n. */ +bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n); + +bool msg_hasFormat(const HvMessage *m, const char *fmt); + +/** + * Create a string representation of the message. Suitable for use by the print object. + * The resulting string must be freed by the caller. + */ +char *msg_toString(const HvMessage *msg); + +#ifdef __cplusplus +} +#endif + +#endif // _HEAVY_MESSAGE_H_ diff --git a/delay_simple/c/HvMessagePool.c b/delay_simple/c/HvMessagePool.c new file mode 100644 index 0000000..dbddb1d --- /dev/null +++ b/delay_simple/c/HvMessagePool.c @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvMessagePool.h" +#include "HvMessage.h" + +// the number of bytes reserved at a time from the pool +#define MP_BLOCK_SIZE_BYTES 512 + +#if HV_APPLE +#pragma mark - MessageList +#endif + +typedef struct MessageListNode { + char *p; + struct MessageListNode *next; +} MessageListNode; + +static inline bool ml_hasAvailable(HvMessagePoolList *ml) { + return (ml->head != NULL); +} + +static char *ml_pop(HvMessagePoolList *ml) { + MessageListNode *n = ml->head; + ml->head = n->next; + n->next = ml->pool; + ml->pool = n; + char *const p = n->p; + n->p = NULL; // set to NULL to make it clear that this node does not have a valid buffer + return p; +} + +/** Push a MessageListNode with the given pointer onto the head of the queue. */ +static void ml_push(HvMessagePoolList *ml, void *p) { + MessageListNode *n = NULL; + if (ml->pool != NULL) { + // take an empty MessageListNode from the pool + n = ml->pool; + ml->pool = n->next; + } else { + // a MessageListNode is not available, allocate one + n = (MessageListNode *) hv_malloc(sizeof(MessageListNode)); + hv_assert(n != NULL); + } + n->p = (char *) p; + n->next = ml->head; + ml->head = n; // push to the front of the queue +} + +static void ml_free(HvMessagePoolList *ml) { + if (ml != NULL) { + while (ml_hasAvailable(ml)) { + ml_pop(ml); + } + while (ml->pool != NULL) { + MessageListNode *n = ml->pool; + ml->pool = n->next; + hv_free(n); + } + } +} + +#if HV_APPLE +#pragma mark - HvMessagePool +#endif + +static hv_size_t mp_messagelistIndexForSize(hv_size_t byteSize) { + return (hv_size_t) hv_max_i((hv_min_max_log2((hv_uint32_t) byteSize) - 5), 0); +} + +hv_size_t mp_init(HvMessagePool *mp, hv_size_t numKB) { + mp->bufferSize = numKB * 1024; + mp->buffer = (char *) hv_malloc(mp->bufferSize); + hv_assert(mp->buffer != NULL); + mp->bufferIndex = 0; + + // initialise all message lists + for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { + mp->lists[i].head = NULL; + mp->lists[i].pool = NULL; + } + + return mp->bufferSize; +} + +void mp_free(HvMessagePool *mp) { + hv_free(mp->buffer); + for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { + ml_free(&mp->lists[i]); + } +} + +void mp_freeMessage(HvMessagePool *mp, HvMessage *m) { + const hv_size_t b = msg_getSize(m); // the number of bytes that a message occupies in memory + const hv_size_t i = mp_messagelistIndexForSize(b); // the HvMessagePoolList index in the pool + HvMessagePoolList *ml = &mp->lists[i]; + const hv_size_t chunkSize = 32 << i; + hv_memclear(m, chunkSize); // clear the chunk, just in case + ml_push(ml, m); +} + +HvMessage *mp_addMessage(HvMessagePool *mp, const HvMessage *m) { + const hv_size_t b = msg_getSize(m); + // determine the message list index to allocate data from based on the msg size + // smallest chunk size is 32 bytes + const hv_size_t i = mp_messagelistIndexForSize(b); + + hv_assert(i < MP_NUM_MESSAGE_LISTS); // how many chunk sizes do we want to support? 32, 64, 128, 256 at the moment + HvMessagePoolList *ml = &mp->lists[i]; + const hv_size_t chunkSize = 32 << i; + + if (ml_hasAvailable(ml)) { + char *buf = ml_pop(ml); + msg_copyToBuffer(m, buf, chunkSize); + return (HvMessage *) buf; + } else { + // if no appropriately sized buffer is immediately available, increase the size of the used buffer + const hv_size_t newIndex = mp->bufferIndex + MP_BLOCK_SIZE_BYTES; + hv_assert((newIndex <= mp->bufferSize) && + "The message pool buffer size has been exceeded. The context cannot store more messages. " + "Try using the new_with_options() initialiser with a larger pool size (default is 10KB)."); + + for (hv_size_t j = mp->bufferIndex; j < newIndex; j += chunkSize) { + ml_push(ml, mp->buffer + j); // push new nodes onto the list with chunk pointers + } + mp->bufferIndex = newIndex; + char *buf = ml_pop(ml); + msg_copyToBuffer(m, buf, chunkSize); + return (HvMessage *) buf; + } +} diff --git a/delay_simple/c/HvMessagePool.h b/delay_simple/c/HvMessagePool.h new file mode 100644 index 0000000..308318e --- /dev/null +++ b/delay_simple/c/HvMessagePool.h @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MESSAGE_POOL_H_ +#define _MESSAGE_POOL_H_ + +#include "HvUtils.h" + +#define MP_NUM_MESSAGE_LISTS 4 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct HvMessagePoolList { + struct MessageListNode *head; // list of currently available blocks + struct MessageListNode *pool; // list of currently used blocks +} HvMessagePoolList; + +typedef struct HvMessagePool { + char *buffer; // the buffer of all messages + hv_size_t bufferSize; // in bytes + hv_size_t bufferIndex; // the number of total reserved bytes + + HvMessagePoolList lists[MP_NUM_MESSAGE_LISTS]; +} HvMessagePool; + +/** + * The HvMessagePool is a basic memory management system. It reserves a large block of memory at initialisation + * and proceeds to divide this block into smaller chunks (usually 512 bytes) as they are needed. These chunks are + * further divided into 32, 64, 128, or 256 sections. Each of these sections is managed by a HvMessagePoolList (MPL). + * An MPL is a linked-list data structure which is initialised such that its own pool of listnodes is filled with nodes + * that point at each subblock (e.g. each 32-byte block of a 512-block chunk). + * + * HvMessagePool is loosely inspired by TCMalloc. http://goog-perftools.sourceforge.net/doc/tcmalloc.html + */ + +hv_size_t mp_init(struct HvMessagePool *mp, hv_size_t numKB); + +void mp_free(struct HvMessagePool *mp); + +/** + * Adds a message to the pool and returns a pointer to the copy. Returns NULL + * if no space was available in the pool. + */ +struct HvMessage *mp_addMessage(struct HvMessagePool *mp, const struct HvMessage *m); + +void mp_freeMessage(struct HvMessagePool *mp, struct HvMessage *m); + +#ifdef __cplusplus +} +#endif + +#endif // _MESSAGE_POOL_H_ diff --git a/delay_simple/c/HvMessageQueue.c b/delay_simple/c/HvMessageQueue.c new file mode 100644 index 0000000..2eeab8f --- /dev/null +++ b/delay_simple/c/HvMessageQueue.c @@ -0,0 +1,215 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvMessageQueue.h" + +hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB) { + hv_assert(poolSizeKB > 0); + q->head = NULL; + q->tail = NULL; + q->pool = NULL; + return mp_init(&q->mp, poolSizeKB); +} + +void mq_free(HvMessageQueue *q) { + mq_clear(q); + while (q->pool != NULL) { + MessageNode *n = q->pool; + q->pool = q->pool->next; + hv_free(n); + } + mp_free(&q->mp); +} + +static MessageNode *mq_getOrCreateNodeFromPool(HvMessageQueue *q) { + if (q->pool == NULL) { + // if necessary, create a new empty node + q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode)); + hv_assert(q->pool != NULL); + q->pool->next = NULL; + } + MessageNode *node = q->pool; + q->pool = q->pool->next; + return node; +} + +int mq_size(HvMessageQueue *q) { + int size = 0; + MessageNode *n = q->head; + while (n != NULL) { + ++size; + n = n->next; + } + return size; +} + +HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + MessageNode *node = mq_getOrCreateNodeFromPool(q); + node->m = mp_addMessage(&q->mp, m); + node->let = let; + node->sendMessage = sendMessage; + node->prev = NULL; + node->next = NULL; + + if (q->tail != NULL) { + // the list already contains elements + q->tail->next = node; + node->prev = q->tail; + q->tail = node; + } else { + // the list is empty + node->prev = NULL; + q->head = node; + q->tail = node; + } + return mq_node_getMessage(node); +} + +HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (mq_hasMessage(q)) { + MessageNode *n = mq_getOrCreateNodeFromPool(q); + n->m = mp_addMessage(&q->mp, m); + n->let = let; + n->sendMessage = sendMessage; + + if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) { + // the message occurs before the current head + n->next = q->head; + q->head->prev = n; + n->prev = NULL; + q->head = n; + } else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) { + // the message occurs after the current tail + n->next = NULL; + n->prev = q->tail; + q->tail->next = n; + q->tail = n; + } else { + // the message occurs somewhere between the head and tail + MessageNode *node = q->head; + while (node != NULL) { + if (msg_getTimestamp(m) < msg_getTimestamp(node->next->m)) { + MessageNode *r = node->next; + node->next = n; + n->next = r; + n->prev = node; + r->prev = n; + break; + } + node = node->next; + } + } + return n->m; + } else { + // add a message to the head + return mq_addMessage(q, m, let, sendMessage); + } +} + +void mq_pop(HvMessageQueue *q) { + if (mq_hasMessage(q)) { + MessageNode *n = q->head; + + mp_freeMessage(&q->mp, n->m); + n->m = NULL; + + n->let = 0; + n->sendMessage = NULL; + + q->head = n->next; + if (q->head == NULL) { + q->tail = NULL; + } else { + q->head->prev = NULL; + } + n->next = q->pool; + n->prev = NULL; + q->pool = n; + } +} + +bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (mq_hasMessage(q)) { + if (mq_node_getMessage(q->head) == m) { // msg in head node + // only remove the message if sendMessage is the same as the stored one, + // if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer + if (sendMessage == NULL || q->head->sendMessage == sendMessage) { + mq_pop(q); + return true; + } + } else { + MessageNode *prevNode = q->head; + MessageNode *currNode = q->head->next; + while ((currNode != NULL) && (currNode->m != m)) { + prevNode = currNode; + currNode = currNode->next; + } + if (currNode != NULL) { + if (sendMessage == NULL || currNode->sendMessage == sendMessage) { + mp_freeMessage(&q->mp, m); + currNode->m = NULL; + currNode->let = 0; + currNode->sendMessage = NULL; + if (currNode == q->tail) { // msg in tail node + prevNode->next = NULL; + q->tail = prevNode; + } else { // msg in middle node + prevNode->next = currNode->next; + currNode->next->prev = prevNode; + } + currNode->next = (q->pool == NULL) ? NULL : q->pool; + currNode->prev = NULL; + q->pool = currNode; + return true; + } + } + } + } + return false; +} + +void mq_clear(HvMessageQueue *q) { + while (mq_hasMessage(q)) { + mq_pop(q); + } +} + +void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp) { + MessageNode *n = q->tail; + while (n != NULL && timestamp <= msg_getTimestamp(n->m)) { + // free the node's message + mp_freeMessage(&q->mp, n->m); + n->m = NULL; + n->let = 0; + n->sendMessage = NULL; + + // the tail points at the previous node + q->tail = n->prev; + + // put the node back in the pool + n->next = q->pool; + n->prev = NULL; + if (q->pool != NULL) q->pool->prev = n; + q->pool = n; + + // update the tail node + n = q->tail; + } + + if (q->tail == NULL) q->head = NULL; +} diff --git a/delay_simple/c/HvMessageQueue.h b/delay_simple/c/HvMessageQueue.h new file mode 100644 index 0000000..a35e4aa --- /dev/null +++ b/delay_simple/c/HvMessageQueue.h @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MESSAGE_QUEUE_H_ +#define _MESSAGE_QUEUE_H_ + +#include "HvMessage.h" +#include "HvMessagePool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +class HeavyContextInterface; +#else +typedef struct HeavyContextInterface HeavyContextInterface; +#endif + +typedef struct MessageNode { + struct MessageNode *prev; // doubly linked list + struct MessageNode *next; + HvMessage *m; + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *); + int let; +} MessageNode; + +/** A doubly linked list containing scheduled messages. */ +typedef struct HvMessageQueue { + MessageNode *head; // the head of the queue + MessageNode *tail; // the tail of the queue + MessageNode *pool; // the head of the reserve pool + HvMessagePool mp; +} HvMessageQueue; + +hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB); + +void mq_free(HvMessageQueue *q); + +int mq_size(HvMessageQueue *q); + +static inline HvMessage *mq_node_getMessage(MessageNode *n) { + return n->m; +} + +static inline int mq_node_getLet(MessageNode *n) { + return n->let; +} + +static inline bool mq_hasMessage(HvMessageQueue *q) { + return (q->head != NULL); +} + +// true if there is a message and it occurs before (<) timestamp +static inline bool mq_hasMessageBefore(HvMessageQueue *const q, const hv_uint32_t timestamp) { + return mq_hasMessage(q) && (msg_getTimestamp(mq_node_getMessage(q->head)) < timestamp); +} + +static inline MessageNode *mq_peek(HvMessageQueue *q) { + return q->head; +} + +/** Appends the message to the end of the queue. */ +HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Insert in ascending order the message acccording to its timestamp. */ +HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Pop the message at the head of the queue (and free its memory). */ +void mq_pop(HvMessageQueue *q); + +/** Remove a message from the queue (and free its memory) */ +bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Clears (and frees) all messages in the queue. */ +void mq_clear(HvMessageQueue *q); + +/** Removes all messages occuring at or after the given timestamp. */ +void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp); + +#ifdef __cplusplus +} +#endif + +#endif // _MESSAGE_QUEUE_H_ diff --git a/delay_simple/c/HvSignalTabread.c b/delay_simple/c/HvSignalTabread.c new file mode 100644 index 0000000..9197cf9 --- /dev/null +++ b/delay_simple/c/HvSignalTabread.c @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvSignalTabread.h" + +hv_size_t sTabread_init(SignalTabread *o, HvTable *table, bool forceAlignedLoads) { + o->table = table; + o->head = 0; + o->forceAlignedLoads = forceAlignedLoads; + return 0; +} + +void sTabread_onMessage(HeavyContextInterface *_c, SignalTabread *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (o->table != NULL) { + switch (msg_getType(m,0)) { + case HV_MSG_BANG: o->head = 0; break; + case HV_MSG_FLOAT: { + hv_uint32_t h = (hv_uint32_t) hv_abs_f(msg_getFloat(m,0)); + if (msg_getFloat(m,0) < 0.0f) { + // if input is negative, wrap around the end of the table + h = hTable_getSize(o->table) - h; + } + o->head = o->forceAlignedLoads ? (h & ~HV_N_SIMD_MASK) : h; + + // output new head + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), (float) o->head); + sendMessage(_c, 1, n); + break; + } + default: break; + } + } + break; + } + case 1: { + if (msg_isHashLike(m,0)) { + o->table = hv_table_get(_c, msg_getHash(m,0)); + } + break; + } + default: break; + } +} + + + +#if HV_APPLE +#pragma mark - Tabhead +#endif + +void sTabhead_onMessage(HeavyContextInterface *_c, SignalTabhead *o, const HvMessage *m) { + if (msg_isHashLike(m,0)) { + o->table = hv_table_get(_c, msg_getHash(m,0)); + } +} + +hv_size_t sTabhead_init(SignalTabhead *o, HvTable *table) { + o->table = table; + return 0; +} diff --git a/delay_simple/c/HvSignalTabread.h b/delay_simple/c/HvSignalTabread.h new file mode 100644 index 0000000..a3d330c --- /dev/null +++ b/delay_simple/c/HvSignalTabread.h @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_SIGNAL_TABREAD_H_ +#define _HEAVY_SIGNAL_TABREAD_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SignalTabread { + HvTable *table; // the table to read + hv_uint32_t head; + bool forceAlignedLoads; // false by default, true if using __hv_tabread_f +} SignalTabread; + +// random access to a table +hv_size_t sTabread_init(SignalTabread *o, HvTable *table, bool forceAlignedLoads); + + + +#if HV_APPLE +#pragma mark - Tabread - Random Access +#endif + +static inline void __hv_tabread_if(SignalTabread *o, hv_bIni_t bIn, hv_bOutf_t bOut) { + const float *const b = hTable_getBuffer(o->table); +#if HV_SIMD_AVX + const hv_int32_t *const i = (hv_int32_t *) &bIn; + + hv_assert(i[0] >= 0 && i[0] < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && i[1] < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && i[2] < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && i[3] < hTable_getAllocated(o->table)); + hv_assert(i[4] >= 0 && i[4] < hTable_getAllocated(o->table)); + hv_assert(i[5] >= 0 && i[5] < hTable_getAllocated(o->table)); + hv_assert(i[6] >= 0 && i[6] < hTable_getAllocated(o->table)); + hv_assert(i[7] >= 0 && i[7] < hTable_getAllocated(o->table)); + + *bOut = _mm256_set_ps(b[i[7]], b[i[6]], b[i[5]], b[i[4]], b[i[3]], b[i[2]], b[i[1]], b[i[0]]); +#elif HV_SIMD_SSE + const hv_int32_t *const i = (hv_int32_t *) &bIn; + + hv_assert(i[0] >= 0 && ((hv_uint32_t) i[0]) < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && ((hv_uint32_t) i[1]) < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && ((hv_uint32_t) i[2]) < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && ((hv_uint32_t) i[3]) < hTable_getAllocated(o->table)); + + *bOut = _mm_set_ps(b[i[3]], b[i[2]], b[i[1]], b[i[0]]); +#elif HV_SIMD_NEON + hv_assert((bIn[0] >= 0) && (bIn[0] < hTable_getAllocated(o->table))); + hv_assert((bIn[1] >= 0) && (bIn[1] < hTable_getAllocated(o->table))); + hv_assert((bIn[2] >= 0) && (bIn[2] < hTable_getAllocated(o->table))); + hv_assert((bIn[3] >= 0) && (bIn[3] < hTable_getAllocated(o->table))); + + *bOut = (float32x4_t) {b[bIn[0]], b[bIn[1]], b[bIn[2]], b[bIn[3]]}; +#else // HV_SIMD_NONE + hv_assert(bIn >= 0 && ((hv_uint32_t) bIn < hTable_getAllocated(o->table))); + + *bOut = b[bIn]; +#endif +} + + + +#if HV_APPLE +#pragma mark - Tabread - Linear Access +#endif + +// this tabread never stops reading. It is mainly intended for linear reads that loop around a table. +static inline void __hv_tabread_f(SignalTabread *o, hv_bOutf_t bOut) { + hv_assert((o->head + HV_N_SIMD) <= hTable_getAllocated(o->table)); // assert that we always read within the table bounds + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + *bOut = _mm256_load_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_SSE + *bOut = _mm_load_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_NEON + *bOut = vld1q_f32(hTable_getBuffer(o->table) + head); +#else // HV_SIMD_NONE + *bOut = *(hTable_getBuffer(o->table) + head); +#endif + o->head = head + HV_N_SIMD; +} + +// unaligned linear tabread, as above +static inline void __hv_tabreadu_f(SignalTabread *o, hv_bOutf_t bOut) { + hv_assert((o->head + HV_N_SIMD) <= hTable_getAllocated(o->table)); // assert that we always read within the table bounds + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + *bOut = _mm256_loadu_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_SSE + *bOut = _mm_loadu_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_NEON + *bOut = vld1q_f32(hTable_getBuffer(o->table) + head); +#else // HV_SIMD_NONE + *bOut = *(hTable_getBuffer(o->table) + head); +#endif + o->head = head + HV_N_SIMD; +} + +// this tabread can be instructed to stop. It is mainly intended for linear reads that only process a portion of a buffer. +static inline void __hv_tabread_stoppable_f(SignalTabread *o, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + if (o->head == ~0x0) { + *bOut = _mm256_setzero_ps(); + } else { + *bOut = _mm256_load_ps(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#elif HV_SIMD_SSE + if (o->head == ~0x0) { + *bOut = _mm_setzero_ps(); + } else { + *bOut = _mm_load_ps(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#elif HV_SIMD_NEON + if (o->head == ~0x0) { + *bOut = vdupq_n_f32(0.0f); + } else { + *bOut = vld1q_f32(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#else // HV_SIMD_NONE + if (o->head == ~0x0) { + *bOut = 0.0f; + } else { + *bOut = *(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#endif +} + +void sTabread_onMessage(HeavyContextInterface *_c, SignalTabread *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + + + +#if HV_APPLE +#pragma mark - Tabhead +#endif + +typedef struct SignalTabhead { + HvTable *table; +} SignalTabhead; + +hv_size_t sTabhead_init(SignalTabhead *o, HvTable *table); + +static inline void __hv_tabhead_f(SignalTabhead *o, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_set1_ps((float) hTable_getHead(o->table)); +#elif HV_SIMD_SSE + *bOut = _mm_set1_ps((float) hTable_getHead(o->table)); +#elif HV_SIMD_NEON + *bOut = vdupq_n_f32((float32_t) hTable_getHead(o->table)); +#else // HV_SIMD_NONE + *bOut = (float) hTable_getHead(o->table); +#endif +} + +void sTabhead_onMessage(HeavyContextInterface *_c, SignalTabhead *o, const HvMessage *m); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_SIGNAL_TABREAD_H_ diff --git a/delay_simple/c/HvSignalTabwrite.c b/delay_simple/c/HvSignalTabwrite.c new file mode 100644 index 0000000..f3d984a --- /dev/null +++ b/delay_simple/c/HvSignalTabwrite.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvSignalTabwrite.h" + +hv_size_t sTabwrite_init(SignalTabwrite *o, HvTable *table) { + o->table = table; + o->head = 0; + return 0; +} + +void sTabwrite_onMessage(HeavyContextInterface *_c, SignalTabwrite *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + // inlet 0 is the signal inlet + case 1: { + switch (msg_getType(m,0)) { + case HV_MSG_BANG: o->head = 0; break; + case HV_MSG_FLOAT: { + o->head = (msg_getFloat(m,0) >= 0.0f) ? (hv_uint32_t) msg_getFloat(m,0) : HV_TABWRITE_STOPPED; + break; + } + case HV_MSG_SYMBOL: { + if (msg_compareSymbol(m, 0, "stop")) { + o->head = HV_TABWRITE_STOPPED; + } + break; + } + default: break; + } + break; + } + case 2: { + if (msg_isHashLike(m,0)) { + o->table = hv_table_get(_c, msg_getHash(m,0)); + } + break; + } + default: break; + } +} diff --git a/delay_simple/c/HvSignalTabwrite.h b/delay_simple/c/HvSignalTabwrite.h new file mode 100644 index 0000000..cadf1e0 --- /dev/null +++ b/delay_simple/c/HvSignalTabwrite.h @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_SIGNAL_TABWRITE_H_ +#define _HEAVY_SIGNAL_TABWRITE_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HV_TABWRITE_STOPPED -1 // ~0x0 + +typedef struct SignalTabwrite { + HvTable *table; + hv_uint32_t head; // local write head. Where this object has most recently written to the table. +} SignalTabwrite; + +hv_size_t sTabwrite_init(SignalTabwrite *o, HvTable *table); + +void sTabwrite_onMessage(HeavyContextInterface *_c, SignalTabwrite *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +// linear write to table +static inline void __hv_tabwrite_f(SignalTabwrite *o, hv_bInf_t bIn) { + hv_assert((o->head + HV_N_SIMD) <= hTable_getSize(o->table)); // assert that the table bounds are respected + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + _mm256_store_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_SSE + _mm_store_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_NEON + vst1q_f32(hTable_getBuffer(o->table) + head, bIn); +#else // HV_SIMD_NONE + *(hTable_getBuffer(o->table) + head) = bIn; +#endif + head += HV_N_SIMD; + o->head = head; // update local write head + hTable_setHead(o->table, head); // update the remote write head (e.g. for use by vd~) +} + +// linear unaligned write to table +static inline void __hv_tabwriteu_f(SignalTabwrite *o, hv_bInf_t bIn) { + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + _mm256_storeu_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_SSE + _mm_storeu_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_NEON + vst1q_f32(hTable_getBuffer(o->table) + head, bIn); +#else // HV_SIMD_NONE + *(hTable_getBuffer(o->table) + head) = bIn; +#endif + head += HV_N_SIMD; + o->head = head; // update local write head + hTable_setHead(o->table, head); // update remote write head +} + +// this tabread can be instructed to stop. It is mainly intended for linear reads that only process a portion of a buffer. +// Stores are unaligned, which can be slow but allows any indicies to be written to. +// TODO(mhroth): this is not stopping! +static inline void __hv_tabwrite_stoppable_f(SignalTabwrite *o, hv_bInf_t bIn) { + if (o->head != HV_TABWRITE_STOPPED) { +#if HV_SIMD_AVX + _mm256_storeu_ps(hTable_getBuffer(o->table) + o->head, bIn); +#elif HV_SIMD_SSE + _mm_storeu_ps(hTable_getBuffer(o->table) + o->head, bIn); +#elif HV_SIMD_NEON + vst1q_f32(hTable_getBuffer(o->table) + o->head, bIn); +#else // HV_SIMD_NONE + *(hTable_getBuffer(o->table) + o->head) = bIn; +#endif + o->head += HV_N_SIMD; + } +} + +// random write to table +static inline void __hv_tabwrite_if(SignalTabwrite *o, hv_bIni_t bIn0, hv_bInf_t bIn1) { + float *const b = hTable_getBuffer(o->table); +#if HV_SIMD_AVX + const hv_int32_t *const i = (hv_int32_t *) &bIn0; + const float *const f = (float *) &bIn1; + + hv_assert(i[0] >= 0 && i[0] < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && i[1] < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && i[2] < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && i[3] < hTable_getAllocated(o->table)); + hv_assert(i[4] >= 0 && i[4] < hTable_getAllocated(o->table)); + hv_assert(i[5] >= 0 && i[5] < hTable_getAllocated(o->table)); + hv_assert(i[6] >= 0 && i[6] < hTable_getAllocated(o->table)); + hv_assert(i[7] >= 0 && i[7] < hTable_getAllocated(o->table)); + + b[i[0]] = f[0]; + b[i[1]] = f[1]; + b[i[2]] = f[2]; + b[i[3]] = f[3]; + b[i[4]] = f[4]; + b[i[5]] = f[5]; + b[i[6]] = f[6]; + b[i[7]] = f[7]; +#elif HV_SIMD_SSE + const hv_int32_t *const i = (hv_int32_t *) &bIn0; + const float *const f = (float *) &bIn1; + + hv_assert(i[0] >= 0 && ((hv_uint32_t) i[0]) < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && ((hv_uint32_t) i[1]) < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && ((hv_uint32_t) i[2]) < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && ((hv_uint32_t) i[3]) < hTable_getAllocated(o->table)); + + b[i[0]] = f[0]; + b[i[1]] = f[1]; + b[i[2]] = f[2]; + b[i[3]] = f[3]; +#elif HV_SIMD_NEON + hv_assert((vgetq_lane_s32(bIn0,0) >= 0) && (vgetq_lane_s32(bIn0,0) < hTable_getSize(o->table))); + hv_assert((vgetq_lane_s32(bIn0,1) >= 0) && (vgetq_lane_s32(bIn0,1) < hTable_getSize(o->table))); + hv_assert((vgetq_lane_s32(bIn0,2) >= 0) && (vgetq_lane_s32(bIn0,2) < hTable_getSize(o->table))); + hv_assert((vgetq_lane_s32(bIn0,3) >= 0) && (vgetq_lane_s32(bIn0,3) < hTable_getSize(o->table))); + + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 0), bIn1, 0); + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 1), bIn1, 1); + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 2), bIn1, 2); + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 3), bIn1, 3); +#else // HV_SIMD_NONE + b[bIn0] = bIn1; +#endif +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_SIGNAL_TABWRITE_H_ diff --git a/delay_simple/c/HvSignalVar.c b/delay_simple/c/HvSignalVar.c new file mode 100644 index 0000000..393cd23 --- /dev/null +++ b/delay_simple/c/HvSignalVar.c @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvSignalVar.h" + +// __var~f + +static void sVarf_update(SignalVarf *o, float k, float step, bool reverse) { +#if HV_SIMD_AVX + if (reverse) o->v = _mm256_setr_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k); + else o->v = _mm256_set_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k); +#elif HV_SIMD_SSE + if (reverse) o->v = _mm_setr_ps(k+3.0f*step, k+2.0f*step, k+step, k); + else o->v = _mm_set_ps(k+3.0f*step, k+2.0f*step, k+step, k); +#elif HV_SIMD_NEON + if (reverse) o->v = (float32x4_t) {3.0f*step+k, 2.0f*step+k, step+k, k}; + else o->v = (float32x4_t) {k, step+k, 2.0f*step+k, 3.0f*step+k}; +#else // HV_SIMD_NONE + o->v = k; +#endif +} + +hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse) { + sVarf_update(o, k, step, reverse); + return 0; +} + +void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m) { + if (msg_isFloat(m,0)) { + sVarf_update(o, msg_getFloat(m,0), msg_isFloat(m,1) ? msg_getFloat(m,1) : 0.0f, msg_getNumElements(m) == 3); + } +} + + + +// __var~i + +static void sVari_update(SignalVari *o, int k, int step, bool reverse) { +#if HV_SIMD_AVX + if (reverse) o->v = _mm256_setr_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k); + else o->v = _mm256_set_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k); +#elif HV_SIMD_SSE + if (reverse) o->v = _mm_setr_epi32(k+3*step, k+2*step, k+step, k); + else o->v = _mm_set_epi32(k+3*step, k+2*step, k+step, k); +#elif HV_SIMD_NEON + if (reverse) o->v = (int32x4_t) {3*step+k, 2*step+k, step+k, k}; + else o->v = (int32x4_t) {k, step+k, 2*step+k, 3*step+k}; +#else // HV_SIMD_NEON + o->v = k; +#endif +} + +hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse) { + sVari_update(o, k, step, reverse); + return 0; +} + +void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m) { + if (msg_isFloat(m,0)) { + sVari_update(o, (int) msg_getFloat(m,0), msg_isFloat(m,1) ? (int) msg_getFloat(m,1) : 0, msg_getNumElements(m) == 3); + } +} diff --git a/delay_simple/c/HvSignalVar.h b/delay_simple/c/HvSignalVar.h new file mode 100644 index 0000000..6502f6b --- /dev/null +++ b/delay_simple/c/HvSignalVar.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_SIGNAL_VAR_H_ +#define _HEAVY_SIGNAL_VAR_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// __var~f, __varread~f, __varwrite~f + +typedef struct SignalVarf { + hv_bufferf_t v; +} SignalVarf; + +hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse); + +static inline void __hv_varread_f(SignalVarf *o, hv_bOutf_t bOut) { + *bOut = o->v; +} + +static inline void __hv_varwrite_f(SignalVarf *o, hv_bInf_t bIn) { + o->v = bIn; +} + +void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m); + + + +// __var~i, __varread~i, __varwrite~i + +typedef struct SignalVari { + hv_bufferi_t v; +} SignalVari; + +hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse); + +static inline void __hv_varread_i(SignalVari *o, hv_bOuti_t bOut) { + *bOut = o->v; +} + +static inline void __hv_varwrite_i(SignalVari *o, hv_bIni_t bIn) { + o->v = bIn; +} + +void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m); + + + +// __var_k~f, __var_k~i + +#if HV_SIMD_AVX +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_h,_g,_f,_e,_d,_c,_b,_a) +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_a,_b,_c,_d,_e,_f,_g,_h) +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_h,_g,_f,_e,_d,_c,_b,_a) +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_a,_b,_c,_d,_e,_f,_g,_h) +#elif HV_SIMD_SSE +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_d,_c,_b,_a) +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_a,_b,_c,_d) +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_d,_c,_b,_a) +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_a,_b,_c,_d) +#elif HV_SIMD_NEON +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_a,_b,_c,_d}) +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_d,_c,_b,_a}) +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_a,_b,_c,_d}) +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_d,_c,_b,_a}) +#else // HV_SIMD_NONE +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_SIGNAL_VAR_H_ diff --git a/delay_simple/c/HvTable.c b/delay_simple/c/HvTable.c new file mode 100644 index 0000000..1b2cd38 --- /dev/null +++ b/delay_simple/c/HvTable.c @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvTable.h" +#include "HvMessage.h" + +hv_size_t hTable_init(HvTable *o, int length) { + o->length = length; + // true size of the table is always an integer multple of HV_N_SIMD + o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; + // add an extra length for mirroring + o->allocated = o->size + HV_N_SIMD; + o->head = 0; + hv_size_t numBytes = o->allocated * sizeof(float); + o->buffer = (float *) hv_malloc(numBytes); + hv_assert(o->buffer != NULL); + hv_memclear(o->buffer, numBytes); + return numBytes; +} + +hv_size_t hTable_initWithData(HvTable *o, int length, const float *data) { + o->length = length; + o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; + o->allocated = o->size + HV_N_SIMD; + o->head = 0; + hv_size_t numBytes = o->size * sizeof(float); + o->buffer = (float *) hv_malloc(numBytes); + hv_assert(o->buffer != NULL); + hv_memclear(o->buffer, numBytes); + hv_memcpy(o->buffer, data, length*sizeof(float)); + return numBytes; +} + +hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data) { + o->length = length; + o->size = length; + o->allocated = length; + o->buffer = data; + o->head = 0; + return 0; +} + +void hTable_free(HvTable *o) { + hv_free(o->buffer); +} + +int hTable_resize(HvTable *o, hv_uint32_t newLength) { + // TODO(mhroth): update context with memory allocated by table + // NOTE(mhroth): mirrored bytes are not necessarily carried over + const hv_uint32_t newSize = (newLength + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; + if (newSize == o->size) return 0; // early exit if no change in size + const hv_uint32_t oldSizeBytes = (hv_uint32_t) (o->size * sizeof(float)); + const hv_uint32_t newAllocated = newSize + HV_N_SIMD; + const hv_uint32_t newAllocatedBytes = (hv_uint32_t) (newAllocated * sizeof(float)); + + float *b = (float *) hv_realloc(o->buffer, newAllocatedBytes); + hv_assert(b != NULL); // error while reallocing! + // ensure that hv_realloc has given us a correctly aligned buffer + if ((((hv_uintptr_t) (const void *) b) & ((0x1< o->size) { + hv_memclear(b + o->size, (newAllocated - o->size) * sizeof(float)); // clear new parts of the buffer + } + o->buffer = b; + } else { + // if not, we have to re-malloc ourselves + char *c = (char *) hv_malloc(newAllocatedBytes); + hv_assert(c != NULL); // error while allocating new buffer! + if (newAllocatedBytes > oldSizeBytes) { + hv_memcpy(c, b, oldSizeBytes); + hv_memclear(c + oldSizeBytes, newAllocatedBytes - oldSizeBytes); + } else { + hv_memcpy(c, b, newAllocatedBytes); + } + hv_free(b); + o->buffer = (float *) c; + } + o->length = newLength; + o->size = newSize; + o->allocated = newAllocated; + return (int) (newAllocated - oldSizeBytes - (HV_N_SIMD*sizeof(float))); +} + +void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (msg_compareSymbol(m,0,"resize") && msg_isFloat(m,1) && msg_getFloat(m,1) >= 0.0f) { + hTable_resize(o, (int) hv_ceil_f(msg_getFloat(m,1))); // apply ceil to ensure that tables always have enough space + + // send out the new size of the table + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(o)); + sendMessage(_c, 0, n); + } + + else if (msg_compareSymbol(m,0,"mirror")) { + hv_memcpy(o->buffer+o->size, o->buffer, HV_N_SIMD*sizeof(float)); + } +} diff --git a/delay_simple/c/HvTable.h b/delay_simple/c/HvTable.h new file mode 100644 index 0000000..1d74887 --- /dev/null +++ b/delay_simple/c/HvTable.h @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_TABLE_H_ +#define _HEAVY_TABLE_H_ + +#include "HvHeavy.h" +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct HvTable { + float *buffer; + // the number of values that the table is requested to have + hv_uint32_t length; + + // the number of usable values that the table actually has + // this is always an even multiple of HV_N_SIMD + hv_uint32_t size; + + // Note that the true size of the table is (size + HV_N_SIMD), + // with the trailing values used by the system, e.g. to create a circular + // buffer + hv_uint32_t allocated; + + hv_uint32_t head; // the most recently written point +} HvTable; + +hv_size_t hTable_init(HvTable *o, int length); + +hv_size_t hTable_initWithData(HvTable *o, int length, const float *data); + +hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data); + +void hTable_free(HvTable *o); + +int hTable_resize(HvTable *o, hv_uint32_t newLength); + +void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +static inline float *hTable_getBuffer(HvTable *o) { + return o->buffer; +} + +// the user-requested length of the table (number of floats) +static inline hv_uint32_t hTable_getLength(HvTable *o) { + return o->length; +} + +// the usable length of the table (an even multiple of HV_N_SIMD) +static inline hv_uint32_t hTable_getSize(HvTable *o) { + return o->size; +} + +// the number of floats allocated to this table (usually size + HV_N_SIMD) +static inline hv_uint32_t hTable_getAllocated(HvTable *o) { + return o->allocated; +} + +static inline hv_uint32_t hTable_getHead(HvTable *o) { + return o->head; +} + +static inline void hTable_setHead(HvTable *o, hv_uint32_t head) { + o->head = head; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_TABLE_H_ diff --git a/delay_simple/c/HvUtils.c b/delay_simple/c/HvUtils.c new file mode 100644 index 0000000..2fdf682 --- /dev/null +++ b/delay_simple/c/HvUtils.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvUtils.h" + +hv_uint32_t hv_string_to_hash(const char *str) { + // this hash is based MurmurHash2 + // http://en.wikipedia.org/wiki/MurmurHash + // https://sites.google.com/site/murmurhash/ + static const hv_uint32_t n = 0x5bd1e995; + static const hv_int32_t r = 24; + + if (str == NULL) return 0; + + hv_uint32_t len = (hv_uint32_t) hv_strlen(str); + hv_uint32_t x = len; // seed (0) ^ len + + while (len >= 4) { +#if HV_EMSCRIPTEN + hv_uint32_t k = str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24); +#else + hv_uint32_t k = *((hv_uint32_t *) str); +#endif + k *= n; + k ^= (k >> r); + k *= n; + x *= n; + x ^= k; + str += 4; len -= 4; + } + switch (len) { + case 3: x ^= (str[2] << 16); + case 2: x ^= (str[1] << 8); + case 1: x ^= str[0]; x *= n; + default: break; + } + x ^= (x >> 13); + x *= n; + x ^= (x >> 15); + return x; +} diff --git a/delay_simple/c/HvUtils.h b/delay_simple/c/HvUtils.h new file mode 100644 index 0000000..3be2432 --- /dev/null +++ b/delay_simple/c/HvUtils.h @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_UTILS_H_ +#define _HEAVY_UTILS_H_ + +// platform definitions +#if _WIN32 || _WIN64 || _MSC_VER + #define HV_WIN 1 +#elif __APPLE__ + #define HV_APPLE 1 +#elif __ANDROID__ + #define HV_ANDROID 1 +#elif __unix__ || __unix + #define HV_UNIX 1 +#else + #warning Could not detect platform. Assuming Unix-like. +#endif + +#ifdef EMSCRIPTEN +#define HV_EMSCRIPTEN 1 +#endif + +// basic includes +#include +#include +#include + +// type definitions +#include +#include +#define hv_uint8_t uint8_t +#define hv_int16_t int16_t +#define hv_uint16_t uint16_t +#define hv_int32_t int32_t +#define hv_uint32_t uint32_t +#define hv_uint64_t uint64_t +#define hv_size_t size_t +#define hv_uintptr_t uintptr_t + +// SIMD-specific includes +#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX) + #define HV_SIMD_NEON __ARM_NEON__ + #define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__) + #define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE) +#endif +#ifndef HV_SIMD_FMA + #define HV_SIMD_FMA __FMA__ +#endif + +#if HV_SIMD_AVX || HV_SIMD_SSE + #include +#elif HV_SIMD_NEON + #include +#endif + +#if HV_SIMD_NEON // NEON + #define HV_N_SIMD 4 + #define hv_bufferf_t float32x4_t + #define hv_bufferi_t int32x4_t + #define hv_bInf_t float32x4_t + #define hv_bOutf_t float32x4_t* + #define hv_bIni_t int32x4_t + #define hv_bOuti_t int32x4_t* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#elif HV_SIMD_AVX // AVX + #define HV_N_SIMD 8 + #define hv_bufferf_t __m256 + #define hv_bufferi_t __m256i + #define hv_bInf_t __m256 + #define hv_bOutf_t __m256* + #define hv_bIni_t __m256i + #define hv_bOuti_t __m256i* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#elif HV_SIMD_SSE // SSE + #define HV_N_SIMD 4 + #define hv_bufferf_t __m128 + #define hv_bufferi_t __m128i + #define hv_bInf_t __m128 + #define hv_bOutf_t __m128* + #define hv_bIni_t __m128i + #define hv_bOuti_t __m128i* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#else // DEFAULT + #define HV_N_SIMD 1 + #undef HV_SIMD_NONE + #define HV_SIMD_NONE 1 + #define hv_bufferf_t float + #define hv_bufferi_t int + #define hv_bInf_t float + #define hv_bOutf_t float* + #define hv_bIni_t int + #define hv_bOuti_t int* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#endif + +#define HV_N_SIMD_MASK (HV_N_SIMD-1) + +// Strings +#include +#define hv_strlen(a) strlen(a) +#define hv_strcmp(a, b) strcmp(a, b) +#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__) +#if HV_WIN +#define hv_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE) +#else +#define hv_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len) +#endif + +// Memory management +#define hv_memcpy(a, b, c) memcpy(a, b, c) +#define hv_memclear(a, b) memset(a, 0, b) +#if HV_WIN + #include + #define hv_alloca(_n) _alloca(_n) + #if HV_SIMD_AVX + #define hv_malloc(_n) _aligned_malloc(_n, 32) + #define hv_realloc(a, b) _aligned_realloc(a, b, 32) + #define hv_free(x) _aligned_free(x) + #elif HV_SIMD_SSE || HV_SIMD_NEON + #define hv_malloc(_n) _aligned_malloc(_n, 16) + #define hv_realloc(a, b) _aligned_realloc(a, b, 16) + #define hv_free(x) _aligned_free(x) + #else // HV_SIMD_NONE + #define hv_malloc(_n) malloc(_n) + #define hv_realloc(a, b) realloc(a, b) + #define hv_free(_n) free(_n) + #endif +#elif HV_APPLE + #define hv_alloca(_n) alloca(_n) + #define hv_realloc(a, b) realloc(a, b) + #if HV_SIMD_AVX + #include + #define hv_malloc(_n) _mm_malloc(_n, 32) + #define hv_free(x) _mm_free(x) + #elif HV_SIMD_SSE + #include + #define hv_malloc(_n) _mm_malloc(_n, 16) + #define hv_free(x) _mm_free(x) + #elif HV_SIMD_NEON + // malloc on ios always has 16-byte alignment + #define hv_malloc(_n) malloc(_n) + #define hv_free(x) free(x) + #else // HV_SIMD_NONE + #define hv_malloc(_n) malloc(_n) + #define hv_free(x) free(x) + #endif +#else + #include + #define hv_alloca(_n) alloca(_n) + #define hv_realloc(a, b) realloc(a, b) + #if HV_SIMD_AVX + #define hv_malloc(_n) aligned_alloc(32, _n) + #define hv_free(x) free(x) + #elif HV_SIMD_SSE + #define hv_malloc(_n) aligned_alloc(16, _n) + #define hv_free(x) free(x) + #elif HV_SIMD_NEON + #if HV_ANDROID + #define hv_malloc(_n) memalign(16, _n) + #define hv_free(x) free(x) + #else + #define hv_malloc(_n) aligned_alloc(16, _n) + #define hv_free(x) free(x) + #endif + #else // HV_SIMD_NONE + #define hv_malloc(_n) malloc(_n) + #define hv_free(_n) free(_n) + #endif +#endif + +// Assert +#include +#define hv_assert(e) assert(e) + +// Export and Inline +#if HV_WIN +#define HV_EXPORT __declspec(dllexport) +#define inline __inline +#define HV_FORCE_INLINE __forceinline +#else +#define HV_EXPORT +#define HV_FORCE_INLINE inline __attribute__((always_inline)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + // Returns a 32-bit hash of any string. Returns 0 if string is NULL. + hv_uint32_t hv_string_to_hash(const char *str); +#ifdef __cplusplus +} +#endif + +// Math +#include +static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; } +static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; } +static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; } +static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; } +#define hv_max_ui(a, b) __hv_utils_max_ui(a, b) +#define hv_min_ui(a, b) __hv_utils_min_ui(a, b) +#define hv_max_i(a, b) __hv_utils_max_i(a, b) +#define hv_min_i(a, b) __hv_utils_min_i(a, b) +#define hv_max_f(a, b) fmaxf(a, b) +#define hv_min_f(a, b) fminf(a, b) +#define hv_max_d(a, b) fmax(a, b) +#define hv_min_d(a, b) fmin(a, b) +#define hv_sin_f(a) sinf(a) +#define hv_sinh_f(a) sinhf(a) +#define hv_cos_f(a) cosf(a) +#define hv_cosh_f(a) coshf(a) +#define hv_tan_f(a) tanf(a) +#define hv_tanh_f(a) tanhf(a) +#define hv_asin_f(a) asinf(a) +#define hv_asinh_f(a) asinhf(a) +#define hv_acos_f(a) acosf(a) +#define hv_acosh_f(a) acoshf(a) +#define hv_atan_f(a) atanf(a) +#define hv_atanh_f(a) atanhf(a) +#define hv_atan2_f(a, b) atan2f(a, b) +#define hv_exp_f(a) expf(a) +#define hv_abs_f(a) fabsf(a) +#define hv_sqrt_f(a) sqrtf(a) +#define hv_log_f(a) logf(a) +#define hv_ceil_f(a) ceilf(a) +#define hv_floor_f(a) floorf(a) +#define hv_round_f(a) roundf(a) +#define hv_pow_f(a, b) powf(a, b) +#if HV_EMSCRIPTEN +#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?) +#else +#define hv_fma_f(a, b, c) fmaf(a, b, c) +#endif +#if HV_WIN + // finds ceil(log2(x)) + #include + static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { + unsigned long z = 0; + _BitScanReverse(&z, x); + return (hv_uint32_t) (z+1); + } +#else + static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { + return (hv_uint32_t) (32 - __builtin_clz(x-1)); + } +#endif +#define hv_min_max_log2(a) __hv_utils_min_max_log2(a) + +// Atomics +#if HV_WIN + #include + #define hv_atomic_bool volatile LONG + #define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { } + #define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false) + #define HV_SPINLOCK_RELEASE(_x) (_x = false) +#elif HV_ANDROID + // Android support for atomics isn't that great, we'll do it manually + // https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html + #define hv_atomic_bool hv_uint8_t + #define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1)) + #define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1) + #define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x) +#elif __cplusplus + #include + #define hv_atomic_bool std::atomic_flag + #define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire)) + #define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire) + #define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release) +#elif defined(__has_include) + #if __has_include() + #include + #define hv_atomic_bool atomic_flag + #define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)) + #define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire) + #define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release) + #endif +#endif +#ifndef hv_atomic_bool + #define hv_atomic_bool volatile bool + #define HV_SPINLOCK_ACQUIRE(_x) \ + while (_x) {} \ + _x = true; + #define HV_SPINLOCK_TRY(_x) \ + if (!_x) { \ + _x = true; \ + return true; \ + } else return false; + #define HV_SPINLOCK_RELEASE(_x) (_x = false) +#endif + +#endif // _HEAVY_UTILS_H_ diff --git a/delay_simple/hv/dpf_delay_simple.hv.json b/delay_simple/hv/dpf_delay_simple.hv.json new file mode 100644 index 0000000..f1c1da7 --- /dev/null +++ b/delay_simple/hv/dpf_delay_simple.hv.json @@ -0,0 +1 @@ +{"type": "graph", "imports": [], "args": [], "objects": {"dac~_ihRMYXa0": {"type": "dac", "args": {"channels": [1, 2]}, "properties": {"x": 88, "y": 247}}, "adc~_tegG45K2": {"type": "adc", "args": {"channels": [1, 2]}, "properties": {"x": 89, "y": 39}}, "s_kpMicFvE": {"type": "send", "args": {"name": "dtime1", "extern": null, "attributes": {}}, "properties": {"x": 373, "y": 160}, "annotations": {"scope": "public"}}, "graph_tqZGB6Cr": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_z6MEltNE": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 35}}, "graph_PZzqtq1o": {"type": "graph", "imports": [], "args": [], "objects": {"comment_aZ0cJb19": {"type": "comment", "args": {"text": "@hv_arg \\$1 k float 0 false"}, "properties": {"x": 11, "y": 119}, "annotations": {}}, "__var_RgnWp7SQ": {"type": "__var", "args": {"k": 0.0}, "properties": {"x": 13, "y": 60}, "annotations": {}}, "outlet_jiNQrRZc": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 90}}, "inlet_heYr1CkA": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 20}}, "inlet_tQ4T7YRg": {"type": "inlet", "args": {"name": "", "index": 1, "type": "-->"}, "properties": {"x": 124, "y": 20}}}, "connections": [{"from": {"id": "__var_RgnWp7SQ", "outlet": 0}, "to": {"id": "outlet_jiNQrRZc", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_heYr1CkA", "outlet": 0}, "to": {"id": "__var_RgnWp7SQ", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_tQ4T7YRg", "outlet": 0}, "to": {"id": "__var_RgnWp7SQ", "inlet": 1}, "type": "-->"}], "properties": {"x": 24, "y": 57}}, "outlet_OtG3t3JQ": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 79}}}, "connections": [{"from": {"id": "inlet_z6MEltNE", "outlet": 0}, "to": {"id": "graph_PZzqtq1o", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_PZzqtq1o", "outlet": 0}, "to": {"id": "outlet_OtG3t3JQ", "inlet": 0}, "type": "-->"}], "properties": {"x": 376, "y": 133}}, "graph_SmBGmyK4": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_MRCJw2vn": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 35}}, "graph_kf1r6G24": {"type": "graph", "imports": [], "args": [], "objects": {"comment_mvhS9Ltq": {"type": "comment", "args": {"text": "@hv_arg \\$1 k float 0 false"}, "properties": {"x": 11, "y": 119}, "annotations": {}}, "__var_lNQLwfEy": {"type": "__var", "args": {"k": 0.0}, "properties": {"x": 13, "y": 60}, "annotations": {}}, "outlet_gw3kv067": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 90}}, "inlet_2UgX3Scr": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 20}}, "inlet_IwzWWGKc": {"type": "inlet", "args": {"name": "", "index": 1, "type": "-->"}, "properties": {"x": 124, "y": 20}}}, "connections": [{"from": {"id": "__var_lNQLwfEy", "outlet": 0}, "to": {"id": "outlet_gw3kv067", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_2UgX3Scr", "outlet": 0}, "to": {"id": "__var_lNQLwfEy", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_IwzWWGKc", "outlet": 0}, "to": {"id": "__var_lNQLwfEy", "inlet": 1}, "type": "-->"}], "properties": {"x": 24, "y": 57}}, "outlet_6O1g3wYJ": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 79}}}, "connections": [{"from": {"id": "inlet_MRCJw2vn", "outlet": 0}, "to": {"id": "graph_kf1r6G24", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_kf1r6G24", "outlet": 0}, "to": {"id": "outlet_6O1g3wYJ", "inlet": 0}, "type": "-->"}], "properties": {"x": 449, "y": 192}}, "graph_7sboNFAF": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_q5WA8ezJ": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 35}}, "graph_EquyXkbu": {"type": "graph", "imports": [], "args": [], "objects": {"comment_CYDAWP8B": {"type": "comment", "args": {"text": "@hv_arg \\$1 k float 0 false"}, "properties": {"x": 11, "y": 119}, "annotations": {}}, "__var_J9cUXuU9": {"type": "__var", "args": {"k": 0.0}, "properties": {"x": 13, "y": 60}, "annotations": {}}, "outlet_yU31dEK3": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 90}}, "inlet_Y4WKphHk": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 20}}, "inlet_83F8Os2B": {"type": "inlet", "args": {"name": "", "index": 1, "type": "-->"}, "properties": {"x": 124, "y": 20}}}, "connections": [{"from": {"id": "__var_J9cUXuU9", "outlet": 0}, "to": {"id": "outlet_yU31dEK3", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_Y4WKphHk", "outlet": 0}, "to": {"id": "__var_J9cUXuU9", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_83F8Os2B", "outlet": 0}, "to": {"id": "__var_J9cUXuU9", "inlet": 1}, "type": "-->"}], "properties": {"x": 24, "y": 57}}, "outlet_kSlYqQnx": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 79}}}, "connections": [{"from": {"id": "inlet_q5WA8ezJ", "outlet": 0}, "to": {"id": "graph_EquyXkbu", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_EquyXkbu", "outlet": 0}, "to": {"id": "outlet_kSlYqQnx", "inlet": 0}, "type": "-->"}], "properties": {"x": 523, "y": 250}}, "s_IDGhe3HM": {"type": "send", "args": {"name": "dvol1", "extern": null, "attributes": {}}, "properties": {"x": 446, "y": 219}, "annotations": {"scope": "public"}}, "s_BLcJU1ta": {"type": "send", "args": {"name": "dfeed1", "extern": null, "attributes": {}}, "properties": {"x": 520, "y": 277}, "annotations": {"scope": "public"}}, "*~_TkMENaWh": {"type": "*", "args": {"k": 0.0}, "properties": {"x": 100, "y": 140}}, "graph_V510iYtt": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_MCvM06IA": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 35}}, "graph_sIbnAyNX": {"type": "graph", "imports": [], "args": [], "objects": {"comment_LMoAz09x": {"type": "comment", "args": {"text": "@hv_arg \\$1 k float 0 false"}, "properties": {"x": 11, "y": 119}, "annotations": {}}, "__var_BdKSM5UN": {"type": "__var", "args": {"k": 0.0}, "properties": {"x": 13, "y": 60}, "annotations": {}}, "outlet_xTNuxXDv": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 90}}, "inlet_xkCoSLch": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 20}}, "inlet_RUT5V6ti": {"type": "inlet", "args": {"name": "", "index": 1, "type": "-->"}, "properties": {"x": 124, "y": 20}}}, "connections": [{"from": {"id": "__var_BdKSM5UN", "outlet": 0}, "to": {"id": "outlet_xTNuxXDv", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_xkCoSLch", "outlet": 0}, "to": {"id": "__var_BdKSM5UN", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_RUT5V6ti", "outlet": 0}, "to": {"id": "__var_BdKSM5UN", "inlet": 1}, "type": "-->"}], "properties": {"x": 24, "y": 57}}, "outlet_xcdOWeyw": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 79}}}, "connections": [{"from": {"id": "inlet_MCvM06IA", "outlet": 0}, "to": {"id": "graph_sIbnAyNX", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_sIbnAyNX", "outlet": 0}, "to": {"id": "outlet_xcdOWeyw", "inlet": 0}, "type": "-->"}], "properties": {"x": 156, "y": 112}}, "s_eNl2WrOm": {"type": "send", "args": {"name": "dryvol", "extern": null, "attributes": {}}, "properties": {"x": 376, "y": 64}, "annotations": {"scope": "public"}}, "graph_cJyXFlhu": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_lv3aPBNH": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 35}}, "graph_MRAsEqnQ": {"type": "graph", "imports": [], "args": [], "objects": {"comment_peA2l95H": {"type": "comment", "args": {"text": "@hv_arg \\$1 k float 0 false"}, "properties": {"x": 11, "y": 119}, "annotations": {}}, "__var_o0Pm8XUl": {"type": "__var", "args": {"k": 0.0}, "properties": {"x": 13, "y": 60}, "annotations": {}}, "outlet_a6fkjWY9": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 90}}, "inlet_mdVJ3Ymn": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 13, "y": 20}}, "inlet_924eoI07": {"type": "inlet", "args": {"name": "", "index": 1, "type": "-->"}, "properties": {"x": 124, "y": 20}}}, "connections": [{"from": {"id": "__var_o0Pm8XUl", "outlet": 0}, "to": {"id": "outlet_a6fkjWY9", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_mdVJ3Ymn", "outlet": 0}, "to": {"id": "__var_o0Pm8XUl", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_924eoI07", "outlet": 0}, "to": {"id": "__var_o0Pm8XUl", "inlet": 1}, "type": "-->"}], "properties": {"x": 24, "y": 57}}, "outlet_Zeq00Kd8": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 24, "y": 79}}}, "connections": [{"from": {"id": "inlet_lv3aPBNH", "outlet": 0}, "to": {"id": "graph_MRAsEqnQ", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_MRAsEqnQ", "outlet": 0}, "to": {"id": "outlet_Zeq00Kd8", "inlet": 0}, "type": "-->"}], "properties": {"x": 379, "y": 37}}, "graph_6PG2OzBZ": {"type": "graph", "imports": [], "args": [], "objects": {"outlet~_EZwnFBJq": {"type": "outlet", "args": {"name": "", "index": 0, "type": "~f>"}, "properties": {"x": 93, "y": 498}}, "inlet~_or58hCOs": {"type": "inlet", "args": {"name": "", "index": 0, "type": "~f>"}, "properties": {"x": 94, "y": 49}}, "r_d1Heztmj": {"type": "receive", "args": {"name": "dtime1", "extern": null, "attributes": {}, "priority": 1999}, "properties": {"x": 171, "y": 309}, "annotations": {"scope": "public"}}, "graph_joZHHHhI": {"type": "graph", "imports": [], "args": [], "objects": {"inlet~_R8cGt4II": {"type": "inlet", "args": {"name": "", "index": 0, "type": "~f>"}, "properties": {"x": 10, "y": 181}}, "__tabwrite~f_XIpb8HJc": {"type": "__tabwrite~f", "args": {"table": "del-delay1012"}, "properties": {"x": 10, "y": 206}, "annotations": {}}, "comment_7HdFE9d8": {"type": "comment", "args": {"text": "@hv_arg \\$2 delay float 0 true"}, "properties": {"x": 9, "y": 253}, "annotations": {}}, "graph_Qf2KgZob": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_tnppCFls": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 19, "y": 11}}, "outlet_s1wuIdVg": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 19, "y": 84}}, "msg_VT3jiKm3": {"type": "message", "args": {"local": [["samplerate"]], "remote": []}, "properties": {"x": 19, "y": 35}}, "system_apQRgBRX": {"type": "system", "args": {}, "properties": {"x": 19, "y": 59}, "annotations": {}}}, "connections": [{"from": {"id": "inlet_tnppCFls", "outlet": 0}, "to": {"id": "msg_VT3jiKm3", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_VT3jiKm3", "outlet": 0}, "to": {"id": "system_apQRgBRX", "inlet": 0}, "type": "-->"}, {"from": {"id": "system_apQRgBRX", "outlet": 0}, "to": {"id": "outlet_s1wuIdVg", "inlet": 0}, "type": "-->"}], "properties": {"x": 187, "y": 31}}, "*_wfA045cu": {"type": "*", "args": {"k": 5000.0}, "properties": {"x": 187, "y": 71}}, "table_3mH8vIOX": {"type": "table", "args": {"name": "del-delay1012"}, "properties": {"x": 187, "y": 111}, "annotations": {"scope": "public"}}, "msg_ntZgzdGM": {"type": "message", "args": {"local": [["resize", "$1"]], "remote": []}, "properties": {"x": 187, "y": 91}}, "comment_vp08L9hI": {"type": "comment", "args": {"text": "@hv_arg \\$1 table string \"\" true"}, "properties": {"x": 9, "y": 234}, "annotations": {}}, "/_QehtRKMg": {"type": "/", "args": {"k": 1000.0}, "properties": {"x": 187, "y": 51}}, "__delay_BP9l7ypB": {"type": "__delay", "args": {}, "properties": {"x": 187, "y": 159}, "annotations": {}}, "graph_R0E0ZAYo": {"type": "graph", "imports": [], "args": [], "objects": {"r_8wA1ZEFP": {"type": "receive", "args": {"name": "__hv_init", "extern": null, "attributes": {}, "priority": 3998}, "properties": {"x": 12, "y": 11}, "annotations": {"scope": "public"}}, "outlet_fQkZNTRN": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 12, "y": 34}}, "comment_i17PYgZ2": {"type": "comment", "args": {"text": "@hv_arg \\$1 priority int 0 false"}, "properties": {"x": 12, "y": 58}, "annotations": {}}}, "connections": [{"from": {"id": "r_8wA1ZEFP", "outlet": 0}, "to": {"id": "outlet_fQkZNTRN", "inlet": 0}, "type": "-->"}], "properties": {"x": 187, "y": 8}}, "trigger_4rdqrLYJ": {"type": "sequence", "args": {"casts": ["a", "a", "a"]}, "properties": {"x": 187, "y": 182}}, "msg_YC3TQ1V2": {"type": "message", "args": {"local": [["mirror"]], "remote": []}, "properties": {"x": 226, "y": 228}}, "__delay_E3a2omWl": {"type": "__delay", "args": {}, "properties": {"x": 226, "y": 206}, "annotations": {}}, "trigger_9iKiqwmR": {"type": "sequence", "args": {"casts": ["b", "a", "a"]}, "properties": {"x": 187, "y": 134}}, "comment_qc0WzeLo": {"type": "comment", "args": {"text": "returns the size of the table"}, "properties": {"x": 254, "y": 90}, "annotations": {}}, "msg_YIsWLHYp": {"type": "message", "args": {"local": [["@HV_N_SIMD"]], "remote": []}, "properties": {"x": 331, "y": 159}}}, "connections": [{"from": {"id": "inlet~_R8cGt4II", "outlet": 0}, "to": {"id": "__tabwrite~f_XIpb8HJc", "inlet": 0}, "type": "~f>"}, {"from": {"id": "graph_Qf2KgZob", "outlet": 0}, "to": {"id": "/_QehtRKMg", "inlet": 0}, "type": "-->"}, {"from": {"id": "*_wfA045cu", "outlet": 0}, "to": {"id": "msg_ntZgzdGM", "inlet": 0}, "type": "-->"}, {"from": {"id": "table_3mH8vIOX", "outlet": 0}, "to": {"id": "trigger_9iKiqwmR", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_ntZgzdGM", "outlet": 0}, "to": {"id": "table_3mH8vIOX", "inlet": 0}, "type": "-->"}, {"from": {"id": "/_QehtRKMg", "outlet": 0}, "to": {"id": "*_wfA045cu", "inlet": 0}, "type": "-->"}, {"from": {"id": "__delay_BP9l7ypB", "outlet": 0}, "to": {"id": "trigger_4rdqrLYJ", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_R0E0ZAYo", "outlet": 0}, "to": {"id": "graph_Qf2KgZob", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_4rdqrLYJ", "outlet": 0}, "to": {"id": "__tabwrite~f_XIpb8HJc", "inlet": 1}, "type": "-->"}, {"from": {"id": "trigger_4rdqrLYJ", "outlet": 1}, "to": {"id": "__delay_BP9l7ypB", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_4rdqrLYJ", "outlet": 2}, "to": {"id": "__delay_E3a2omWl", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_YC3TQ1V2", "outlet": 0}, "to": {"id": "table_3mH8vIOX", "inlet": 0}, "type": "-->"}, {"from": {"id": "__delay_E3a2omWl", "outlet": 0}, "to": {"id": "msg_YC3TQ1V2", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_9iKiqwmR", "outlet": 0}, "to": {"id": "__delay_BP9l7ypB", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_9iKiqwmR", "outlet": 1}, "to": {"id": "__delay_BP9l7ypB", "inlet": 2}, "type": "-->"}, {"from": {"id": "trigger_9iKiqwmR", "outlet": 2}, "to": {"id": "msg_YIsWLHYp", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_YIsWLHYp", "outlet": 0}, "to": {"id": "__delay_E3a2omWl", "inlet": 2}, "type": "-->"}], "properties": {"x": 135, "y": 125}}, "graph_HohGqrRe": {"type": "graph", "imports": [], "args": [], "objects": {"__tabhead_fxpaZxc8": {"type": "__tabhead", "args": {"table": "del-delay1012"}, "properties": {"x": 17, "y": 86}, "annotations": {}}, "graph_ixXKkQHY": {"type": "graph", "imports": [], "args": [], "objects": {"inlet_JgV8U7wZ": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 19, "y": 11}}, "outlet_9WYBmUuN": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 19, "y": 84}}, "msg_9x9zi4mQ": {"type": "message", "args": {"local": [["samplerate"]], "remote": []}, "properties": {"x": 19, "y": 35}}, "system_B7KTx15e": {"type": "system", "args": {}, "properties": {"x": 19, "y": 59}, "annotations": {}}}, "connections": [{"from": {"id": "inlet_JgV8U7wZ", "outlet": 0}, "to": {"id": "msg_9x9zi4mQ", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_9x9zi4mQ", "outlet": 0}, "to": {"id": "system_B7KTx15e", "inlet": 0}, "type": "-->"}, {"from": {"id": "system_B7KTx15e", "outlet": 0}, "to": {"id": "outlet_9WYBmUuN", "inlet": 0}, "type": "-->"}], "properties": {"x": 32, "y": 111}}, "comment_cQ0We6jV": {"type": "comment", "args": {"text": "ms to samples"}, "properties": {"x": 66, "y": 156}, "annotations": {}}, "*_tk6D3nSz": {"type": "*", "args": {"k": 0.0}, "properties": {"x": 32, "y": 156}}, "/_eyuqedJ6": {"type": "/", "args": {"k": 1000.0}, "properties": {"x": 32, "y": 134}}, "-_aerBGz64": {"type": "-", "args": {"k": 0.0}, "properties": {"x": 17, "y": 226}}, "system_rnXo1PY2": {"type": "system", "args": {}, "properties": {"x": 83, "y": 298}, "annotations": {}}, "msg_6gTXWp8k": {"type": "message", "args": {"local": [["table", "$1", "size"]], "remote": []}, "properties": {"x": 83, "y": 274}}, "__var_ywEtiJao": {"type": "__var", "args": {"k": "del-delay1012"}, "properties": {"x": 83, "y": 251}, "annotations": {}}, "__delay_NqdX222X": {"type": "__delay", "args": {"delay": 0.0}, "properties": {"x": 87, "y": 436}, "annotations": {}}, "outlet~_WGuu1z9b": {"type": "outlet", "args": {"name": "", "index": 0, "type": "~f>"}, "properties": {"x": 75, "y": 520}}, "comment_Ohz7tmsU": {"type": "comment", "args": {"text": "@hv_arg \\$2 delay float 10 true"}, "properties": {"x": 74, "y": 562}, "annotations": {}}, "comment_8ws8WRPN": {"type": "comment", "args": {"text": "@hv_arg \\$1 table string \"\" true"}, "properties": {"x": 74, "y": 543}, "annotations": {}}, "msg_QKqmKUhv": {"type": "message", "args": {"local": [["clear"]], "remote": []}, "properties": {"x": 95, "y": 407}}, "trigger_cTVCZqk2": {"type": "sequence", "args": {"casts": ["a", "a"]}, "properties": {"x": 224, "y": 469}}, "__delay_aVkvNBE7": {"type": "__delay", "args": {}, "properties": {"x": 87, "y": 469}, "annotations": {}}, "inlet_xgNzK2wv": {"type": "inlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 95, "y": 6}}, "graph_4Am0E0Xr": {"type": "graph", "imports": [], "args": [], "objects": {"r_DwR0UF3D": {"type": "receive", "args": {"name": "__hv_init", "extern": null, "attributes": {}, "priority": -1}, "properties": {"x": 12, "y": 11}, "annotations": {"scope": "public"}}, "outlet_48xvmJsq": {"type": "outlet", "args": {"name": "", "index": 0, "type": "-->"}, "properties": {"x": 12, "y": 34}}, "comment_MSEI1Yuu": {"type": "comment", "args": {"text": "@hv_arg \\$1 priority int 0 false"}, "properties": {"x": 12, "y": 58}, "annotations": {}}}, "connections": [{"from": {"id": "r_DwR0UF3D", "outlet": 0}, "to": {"id": "outlet_48xvmJsq", "inlet": 0}, "type": "-->"}], "properties": {"x": 17, "y": 6}}, "comment_MW0OlVym": {"type": "comment", "args": {"text": "NOTE(mhroth): there may be an issue here in which"}, "properties": {"x": 205, "y": 7}, "annotations": {}}, "comment_wXYwMcAa": {"type": "comment", "args": {"text": "tabread sets itself to a different offset than"}, "properties": {"x": 223, "y": 24}, "annotations": {}}, "comment_zOfS9c2H": {"type": "comment", "args": {"text": "the input \\, due to alignment issues."}, "properties": {"x": 224, "y": 42}, "annotations": {}}, "comment_YaraoEnY": {"type": "comment", "args": {"text": "But the first delay is set based on the original offset"}, "properties": {"x": 224, "y": 58}, "annotations": {}}, "comment_eecKhDIK": {"type": "comment", "args": {"text": "which may account for the difference of a few samples"}, "properties": {"x": 224, "y": 74}, "annotations": {}}, "msg_sZQ9XVm6": {"type": "message", "args": {"local": [["@HV_N_SIMD"]], "remote": []}, "properties": {"x": 47, "y": 178}}, "comment_QpOln3MX": {"type": "comment", "args": {"text": "add minimum delay of HV_N_SIMD"}, "properties": {"x": 56, "y": 201}, "annotations": {}}, "max_AR2iRmH8": {"type": "max", "args": {"k": 0.0}, "properties": {"x": 32, "y": 200}}, "trigger_F9DRTuTv": {"type": "sequence", "args": {"casts": ["b", "a", "a"]}, "properties": {"x": 56, "y": 345}}, "trigger_7lgi32QF": {"type": "sequence", "args": {"casts": ["a", "a"]}, "properties": {"x": 17, "y": 32}}, "trigger_oJQ81a93": {"type": "sequence", "args": {"casts": ["a", "a", "a"]}, "properties": {"x": 17, "y": 57}}, "*_YKMSAoNC": {"type": "*", "args": {"k": -1.0}, "properties": {"x": 210, "y": 412}}, "-_2myF9DZt": {"type": "-", "args": {"k": 0.0}, "properties": {"x": 210, "y": 391}}, "comment_vbL4DzUP": {"type": "comment", "args": {"text": "delay from one end of the table to the other"}, "properties": {"x": 263, "y": 469}, "annotations": {}}, "comment_qAa7SG4z": {"type": "comment", "args": {"text": "delay from current head to the end of the table"}, "properties": {"x": 222, "y": 435}, "annotations": {}}, "trigger_qVVoEy36": {"type": "sequence", "args": {"casts": ["b", "a"]}, "properties": {"x": 95, "y": 31}}, "__tabread~f_jhXKi3SZ": {"type": "__tabread~f", "args": {"table": "del-delay1012"}, "properties": {"x": 75, "y": 497}, "annotations": {}}}, "connections": [{"from": {"id": "__tabhead_fxpaZxc8", "outlet": 0}, "to": {"id": "-_aerBGz64", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_ixXKkQHY", "outlet": 0}, "to": {"id": "/_eyuqedJ6", "inlet": 0}, "type": "-->"}, {"from": {"id": "*_tk6D3nSz", "outlet": 0}, "to": {"id": "max_AR2iRmH8", "inlet": 0}, "type": "-->"}, {"from": {"id": "/_eyuqedJ6", "outlet": 0}, "to": {"id": "*_tk6D3nSz", "inlet": 0}, "type": "-->"}, {"from": {"id": "-_aerBGz64", "outlet": 0}, "to": {"id": "trigger_F9DRTuTv", "inlet": 0}, "type": "-->"}, {"from": {"id": "system_rnXo1PY2", "outlet": 0}, "to": {"id": "-_2myF9DZt", "inlet": 1}, "type": "-->"}, {"from": {"id": "system_rnXo1PY2", "outlet": 0}, "to": {"id": "__delay_aVkvNBE7", "inlet": 2}, "type": "-->"}, {"from": {"id": "msg_6gTXWp8k", "outlet": 0}, "to": {"id": "system_rnXo1PY2", "inlet": 0}, "type": "-->"}, {"from": {"id": "__var_ywEtiJao", "outlet": 0}, "to": {"id": "msg_6gTXWp8k", "inlet": 0}, "type": "-->"}, {"from": {"id": "__delay_NqdX222X", "outlet": 0}, "to": {"id": "__delay_aVkvNBE7", "inlet": 0}, "type": "-->"}, {"from": {"id": "__delay_NqdX222X", "outlet": 0}, "to": {"id": "__tabread~f_jhXKi3SZ", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_QKqmKUhv", "outlet": 0}, "to": {"id": "__delay_NqdX222X", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_QKqmKUhv", "outlet": 0}, "to": {"id": "__delay_aVkvNBE7", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_cTVCZqk2", "outlet": 0}, "to": {"id": "__delay_aVkvNBE7", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_cTVCZqk2", "outlet": 1}, "to": {"id": "__tabread~f_jhXKi3SZ", "inlet": 0}, "type": "-->"}, {"from": {"id": "__delay_aVkvNBE7", "outlet": 0}, "to": {"id": "trigger_cTVCZqk2", "inlet": 0}, "type": "-->"}, {"from": {"id": "inlet_xgNzK2wv", "outlet": 0}, "to": {"id": "trigger_qVVoEy36", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_4Am0E0Xr", "outlet": 0}, "to": {"id": "trigger_7lgi32QF", "inlet": 0}, "type": "-->"}, {"from": {"id": "msg_sZQ9XVm6", "outlet": 0}, "to": {"id": "max_AR2iRmH8", "inlet": 1}, "type": "-->"}, {"from": {"id": "max_AR2iRmH8", "outlet": 0}, "to": {"id": "-_aerBGz64", "inlet": 1}, "type": "-->"}, {"from": {"id": "trigger_F9DRTuTv", "outlet": 0}, "to": {"id": "__delay_NqdX222X", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_F9DRTuTv", "outlet": 1}, "to": {"id": "__tabread~f_jhXKi3SZ", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_F9DRTuTv", "outlet": 2}, "to": {"id": "msg_QKqmKUhv", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_7lgi32QF", "outlet": 0}, "to": {"id": "trigger_oJQ81a93", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_7lgi32QF", "outlet": 1}, "to": {"id": "msg_sZQ9XVm6", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_oJQ81a93", "outlet": 0}, "to": {"id": "__tabhead_fxpaZxc8", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_oJQ81a93", "outlet": 1}, "to": {"id": "graph_ixXKkQHY", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_oJQ81a93", "outlet": 2}, "to": {"id": "__var_ywEtiJao", "inlet": 0}, "type": "-->"}, {"from": {"id": "*_YKMSAoNC", "outlet": 0}, "to": {"id": "__delay_NqdX222X", "inlet": 2}, "type": "-->"}, {"from": {"id": "-_2myF9DZt", "outlet": 0}, "to": {"id": "*_YKMSAoNC", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_qVVoEy36", "outlet": 0}, "to": {"id": "trigger_oJQ81a93", "inlet": 0}, "type": "-->"}, {"from": {"id": "trigger_qVVoEy36", "outlet": 1}, "to": {"id": "*_tk6D3nSz", "inlet": 1}, "type": "-->"}, {"from": {"id": "__tabread~f_jhXKi3SZ", "outlet": 0}, "to": {"id": "outlet~_WGuu1z9b", "inlet": 0}, "type": "~f>"}, {"from": {"id": "__tabread~f_jhXKi3SZ", "outlet": 1}, "to": {"id": "-_2myF9DZt", "inlet": 0}, "type": "-->"}], "properties": {"x": 171, "y": 339}}, "*~_mzLucyeH": {"type": "*", "args": {"k": 0.0}, "properties": {"x": 170, "y": 427}}, "r_fftIcjVb": {"type": "receive", "args": {"name": "dvol1", "extern": null, "attributes": {}, "priority": 1996}, "properties": {"x": 188, "y": 400}, "annotations": {"scope": "public"}}, "*~_YvqjcaFI": {"type": "*", "args": {"k": 0.0}, "properties": {"x": 274, "y": 402}}, "s~_IKLAmsd7": {"type": "send", "args": {"name": "sndrcv_sig_feedback1012", "extern": null, "attributes": {}}, "properties": {"x": 274, "y": 427}, "annotations": {"scope": "public"}}, "r~_RBQfshMr": {"type": "receive", "args": {"name": "sndrcv_sig_feedback1012", "extern": null, "attributes": {}, "priority": 1995}, "properties": {"x": 158, "y": 79}, "annotations": {"scope": "public"}}, "r_yXojPcao": {"type": "receive", "args": {"name": "dfeed1", "extern": null, "attributes": {}, "priority": 1994}, "properties": {"x": 292, "y": 375}, "annotations": {"scope": "public"}}, "*~_QzebolJs": {"type": "*", "args": {"k": 0.0}, "properties": {"x": 94, "y": 230}}, "r_vJ5lEMrG": {"type": "receive", "args": {"name": "dryvol", "extern": null, "attributes": {}, "priority": 1993}, "properties": {"x": 112, "y": 195}, "annotations": {"scope": "public"}}, "var_xrPkzeK2": {"type": "var", "args": {"k": 0.0}, "properties": {"x": 170, "y": 422}, "annotations": {}}, "var_v3h01NH9": {"type": "var", "args": {"k": 0.0}, "properties": {"x": 274, "y": 397}, "annotations": {}}, "var_kqyU3n6x": {"type": "var", "args": {"k": 0.0}, "properties": {"x": 94, "y": 225}, "annotations": {}}}, "connections": [{"from": {"id": "inlet~_or58hCOs", "outlet": 0}, "to": {"id": "graph_joZHHHhI", "inlet": 0}, "type": "~f>"}, {"from": {"id": "inlet~_or58hCOs", "outlet": 0}, "to": {"id": "*~_QzebolJs", "inlet": 0}, "type": "~f>"}, {"from": {"id": "r_d1Heztmj", "outlet": 0}, "to": {"id": "graph_HohGqrRe", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_HohGqrRe", "outlet": 0}, "to": {"id": "*~_mzLucyeH", "inlet": 0}, "type": "~f>"}, {"from": {"id": "graph_HohGqrRe", "outlet": 0}, "to": {"id": "*~_YvqjcaFI", "inlet": 0}, "type": "~f>"}, {"from": {"id": "*~_mzLucyeH", "outlet": 0}, "to": {"id": "outlet~_EZwnFBJq", "inlet": 0}, "type": "~f>"}, {"from": {"id": "*~_YvqjcaFI", "outlet": 0}, "to": {"id": "s~_IKLAmsd7", "inlet": 0}, "type": "~f>"}, {"from": {"id": "r~_RBQfshMr", "outlet": 0}, "to": {"id": "graph_joZHHHhI", "inlet": 0}, "type": "~f>"}, {"from": {"id": "*~_QzebolJs", "outlet": 0}, "to": {"id": "outlet~_EZwnFBJq", "inlet": 0}, "type": "~f>"}, {"from": {"id": "var_xrPkzeK2", "outlet": 0}, "to": {"id": "*~_mzLucyeH", "inlet": 1}, "type": "~f>"}, {"from": {"id": "r_fftIcjVb", "outlet": 0}, "to": {"id": "var_xrPkzeK2", "inlet": 0}, "type": "-->"}, {"from": {"id": "var_v3h01NH9", "outlet": 0}, "to": {"id": "*~_YvqjcaFI", "inlet": 1}, "type": "~f>"}, {"from": {"id": "r_yXojPcao", "outlet": 0}, "to": {"id": "var_v3h01NH9", "inlet": 0}, "type": "-->"}, {"from": {"id": "var_kqyU3n6x", "outlet": 0}, "to": {"id": "*~_QzebolJs", "inlet": 1}, "type": "~f>"}, {"from": {"id": "r_vJ5lEMrG", "outlet": 0}, "to": {"id": "var_kqyU3n6x", "inlet": 0}, "type": "-->"}], "properties": {"x": 99, "y": 169}}, "r_PwtcspGH": {"type": "receive", "args": {"name": "Gain", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}, "priority": 992}, "properties": {"x": 153, "y": 84}, "annotations": {"scope": "public"}}, "r_4thrsZaj": {"type": "receive", "args": {"name": "Dry_Volume", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}, "priority": 991}, "properties": {"x": 376, "y": 8}, "annotations": {"scope": "public"}}, "r_XtvSSi6s": {"type": "receive", "args": {"name": "Delay_Time", "extern": "param", "attributes": {"min": 0.0, "max": 5000.0, "default": 500.0, "type": "float"}, "priority": 990}, "properties": {"x": 373, "y": 104}, "annotations": {"scope": "public"}}, "r_ScjqP0PR": {"type": "receive", "args": {"name": "Wet_Volume", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}, "priority": 989}, "properties": {"x": 446, "y": 163}, "annotations": {"scope": "public"}}, "r_Ej51FckA": {"type": "receive", "args": {"name": "Delay_Feedback", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.25, "type": "float"}, "priority": 988}, "properties": {"x": 520, "y": 221}, "annotations": {"scope": "public"}}, "var_xMu0S8JF": {"type": "var", "args": {"k": 0.0}, "properties": {"x": 100, "y": 135}, "annotations": {}}}, "connections": [{"from": {"id": "adc~_tegG45K2", "outlet": 0}, "to": {"id": "*~_TkMENaWh", "inlet": 0}, "type": "~f>"}, {"from": {"id": "adc~_tegG45K2", "outlet": 1}, "to": {"id": "*~_TkMENaWh", "inlet": 0}, "type": "~f>"}, {"from": {"id": "graph_tqZGB6Cr", "outlet": 0}, "to": {"id": "s_kpMicFvE", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_SmBGmyK4", "outlet": 0}, "to": {"id": "s_IDGhe3HM", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_7sboNFAF", "outlet": 0}, "to": {"id": "s_BLcJU1ta", "inlet": 0}, "type": "-->"}, {"from": {"id": "*~_TkMENaWh", "outlet": 0}, "to": {"id": "graph_6PG2OzBZ", "inlet": 0}, "type": "~f>"}, {"from": {"id": "graph_cJyXFlhu", "outlet": 0}, "to": {"id": "s_eNl2WrOm", "inlet": 0}, "type": "-->"}, {"from": {"id": "graph_6PG2OzBZ", "outlet": 0}, "to": {"id": "dac~_ihRMYXa0", "inlet": 0}, "type": "~f>"}, {"from": {"id": "graph_6PG2OzBZ", "outlet": 0}, "to": {"id": "dac~_ihRMYXa0", "inlet": 1}, "type": "~f>"}, {"from": {"id": "r_PwtcspGH", "outlet": 0}, "to": {"id": "graph_V510iYtt", "inlet": 0}, "type": "-->"}, {"from": {"id": "r_4thrsZaj", "outlet": 0}, "to": {"id": "graph_cJyXFlhu", "inlet": 0}, "type": "-->"}, {"from": {"id": "r_XtvSSi6s", "outlet": 0}, "to": {"id": "graph_tqZGB6Cr", "inlet": 0}, "type": "-->"}, {"from": {"id": "r_ScjqP0PR", "outlet": 0}, "to": {"id": "graph_SmBGmyK4", "inlet": 0}, "type": "-->"}, {"from": {"id": "r_Ej51FckA", "outlet": 0}, "to": {"id": "graph_7sboNFAF", "inlet": 0}, "type": "-->"}, {"from": {"id": "var_xMu0S8JF", "outlet": 0}, "to": {"id": "*~_TkMENaWh", "inlet": 1}, "type": "~f>"}, {"from": {"id": "graph_V510iYtt", "outlet": 0}, "to": {"id": "var_xMu0S8JF", "inlet": 0}, "type": "-->"}], "properties": {"x": 0, "y": 0}} \ No newline at end of file diff --git a/delay_simple/ir/delay_simple.heavy.ir.json b/delay_simple/ir/delay_simple.heavy.ir.json new file mode 100644 index 0000000..318d579 --- /dev/null +++ b/delay_simple/ir/delay_simple.heavy.ir.json @@ -0,0 +1 @@ +{"name": {"escaped": "delay_simple", "display": "delay_simple"}, "objects": {"4cpXTaBY": {"args": {"k": 0.0}, "type": "__var"}, "fRPZcVCt": {"args": {"k": 0.0}, "type": "__var"}, "Om7ifKfd": {"args": {"k": 0.0}, "type": "__var"}, "LnHfGF8V": {"args": {"k": 0.0}, "type": "__var"}, "mOyup5ir": {"args": {"k": 0.0}, "type": "__var"}, "kTfJPrs1": {"args": {"table": "del-delay1012", "table_id": "TFqOKFso"}, "type": "__tabwrite~f"}, "hdrV3jKk": {"args": {"local": [["samplerate"]], "remote": []}, "type": "__message"}, "B288dglo": {"args": {}, "type": "__system"}, "OFD5iAqe": {"args": {"delay": 0}, "type": "__delay"}, "EI4kKdvf": {"args": {"delay": 0}, "type": "__delay"}, "4MyuHNhK": {"args": {"k": 5000.0}, "type": "__mul_k"}, "TFqOKFso": {"args": {"name": "del-delay1012", "size": 256, "values": [], "extern": false}, "type": "__table"}, "clwxczYs": {"args": {"local": [["resize", "$1"]], "remote": []}, "type": "__message"}, "NAf7xjjy": {"args": {"k": 1000.0}, "type": "__div_k"}, "o1Fk4mWr": {"args": {"local": [["mirror"]], "remote": []}, "type": "__message"}, "g87DeymK": {"args": {}, "type": "__cast_b"}, "DlDeZMFB": {"args": {"local": [["@HV_N_SIMD"]], "remote": []}, "type": "__message"}, "zhu6BFdy": {"args": {"table": "del-delay1012", "table_id": "TFqOKFso"}, "type": "__tabhead"}, "c68iDIVD": {"args": {"local": [["samplerate"]], "remote": []}, "type": "__message"}, "LlBI3RTT": {"args": {}, "type": "__system"}, "nONxuBGP": {"args": {"k": "del-delay1012"}, "type": "__var"}, "FTWI9Lbr": {"args": {"delay": 0.0}, "type": "__delay"}, "4Y58Ksfn": {"args": {"delay": 0}, "type": "__delay"}, "OJVVdLuf": {"args": {"table": "del-delay1012", "table_id": "TFqOKFso"}, "type": "__tabread~f"}, "kd9uMvnd": {"args": {"k": 0.0}, "type": "__mul"}, "qp875S9R": {"args": {"k": 1000.0}, "type": "__div_k"}, "99kJjPmN": {"args": {"k": 0.0}, "type": "__sub"}, "kPqFOU4H": {"args": {}, "type": "__system"}, "2Y6gSDxh": {"args": {"local": [["table", "$1", "size"]], "remote": []}, "type": "__message"}, "60hMCcpx": {"args": {"local": [["clear"]], "remote": []}, "type": "__message"}, "2y7SxePc": {"args": {"local": [["@HV_N_SIMD"]], "remote": []}, "type": "__message"}, "khIQbSXe": {"args": {"k": 0.0}, "type": "__max"}, "zoAHkSwC": {"args": {}, "type": "__cast_b"}, "H69XkGIL": {"args": {"k": -1.0}, "type": "__mul_k"}, "HsqyfMPx": {"args": {"k": 0.0}, "type": "__sub"}, "spzeS2Y3": {"args": {}, "type": "__cast_b"}, "DXN3F4mN": {"args": {"k": 0, "step": 0, "reverse": false}, "type": "__var~f"}, "72seEPQr": {"args": {"var_id": "DXN3F4mN"}, "type": "__varwrite~f"}, "1E66K3Ry": {"args": {"var_id": "DXN3F4mN"}, "type": "__varread~f"}, "aZPZjXe3": {"args": {}, "type": "__mul~f"}, "XBUwwqqx": {"args": {}, "type": "__mul~f"}, "o7lB6W7R": {"args": {"k": 0.0, "step": 0.0, "reverse": false, "name": null}, "type": "__var~f"}, "59xeVZ71": {"args": {"var_id": "o7lB6W7R"}, "type": "__varread~f"}, "4RDRwlaL": {"args": {"k": 0.0, "step": 0.0, "reverse": false, "name": null}, "type": "__var~f"}, "QLbNvMZU": {"args": {"var_id": "4RDRwlaL"}, "type": "__varread~f"}, "vL8TEPN9": {"args": {"k": 0.0, "step": 0.0, "reverse": false, "name": null}, "type": "__var~f"}, "yUsjcNYR": {"args": {"var_id": "vL8TEPN9"}, "type": "__varread~f"}, "D6wsFINb": {"args": {}, "type": "__add~f"}, "DKIa3dd2": {"args": {}, "type": "__fma~f"}, "iIE4ePU1": {"args": {}, "type": "__add~f"}, "bbsF8nB6": {"args": {}, "type": "__add~f"}, "wldZJDJM": {"args": {"name": "dtime1", "extern": null, "attributes": {}, "hash": "0x37BBAC62"}, "type": "__send"}, "dFhbvIPr": {"args": {"name": "dvol1", "extern": null, "attributes": {}, "hash": "0xCA9AE94A"}, "type": "__send"}, "4A35hG1G": {"args": {"name": "dfeed1", "extern": null, "attributes": {}, "hash": "0xD8A3CB65"}, "type": "__send"}, "CfeUbU9R": {"args": {}, "type": "__mul~f"}, "jiugLxbD": {"args": {"name": "dryvol", "extern": null, "attributes": {}, "hash": "0xAEFD47D3"}, "type": "__send"}, "eLw9NCKn": {"args": {"k": 0.0, "step": 0.0, "reverse": false, "name": null}, "type": "__var~f"}, "38jlXfLk": {"args": {"var_id": "eLw9NCKn"}, "type": "__varread~f"}, "GGTMonx0": {"args": {}, "type": "__add~f"}, "kE9u8lIG": {"args": {"name": "dtime1", "extern": null, "attributes": {}}, "type": "__receive"}, "pzAN0z6v": {"args": {"name": "dvol1", "extern": null, "attributes": {}}, "type": "__receive"}, "iG7Q7Uzr": {"args": {"name": "dfeed1", "extern": null, "attributes": {}}, "type": "__receive"}, "kJYDDgog": {"args": {"name": "dryvol", "extern": null, "attributes": {}}, "type": "__receive"}, "AJBhI0kX": {"args": {"name": "__hv_init", "extern": null, "attributes": {}}, "type": "__receive"}, "eGza5oBR": {"args": {"name": "Gain", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}}, "type": "__receive"}, "SIoX2X2C": {"args": {"name": "Dry_Volume", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}}, "type": "__receive"}, "UQL9KHRi": {"args": {"name": "Delay_Time", "extern": "param", "attributes": {"min": 0.0, "max": 5000.0, "default": 500.0, "type": "float"}}, "type": "__receive"}, "ex2b75Oc": {"args": {"name": "Wet_Volume", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}}, "type": "__receive"}, "LQwqfjMo": {"args": {"name": "Delay_Feedback", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.25, "type": "float"}}, "type": "__receive"}}, "init": {"order": ["OJVVdLuf", "kTfJPrs1", "4cpXTaBY", "fRPZcVCt", "Om7ifKfd", "LnHfGF8V", "mOyup5ir", "OFD5iAqe", "EI4kKdvf", "4MyuHNhK", "TFqOKFso", "NAf7xjjy", "zhu6BFdy", "nONxuBGP", "FTWI9Lbr", "4Y58Ksfn", "kd9uMvnd", "qp875S9R", "99kJjPmN", "khIQbSXe", "H69XkGIL", "HsqyfMPx", "DXN3F4mN", "o7lB6W7R", "4RDRwlaL", "vL8TEPN9", "eLw9NCKn"]}, "tables": {"del-delay1012": {"id": "TFqOKFso", "display": "del-delay1012", "hash": "0x4678FF81", "extern": false}}, "control": {"receivers": {"dtime1": {"display": "dtime1", "hash": "0x37BBAC62", "extern": null, "attributes": {}, "ids": ["kE9u8lIG"]}, "dvol1": {"display": "dvol1", "hash": "0xCA9AE94A", "extern": null, "attributes": {}, "ids": ["pzAN0z6v"]}, "dfeed1": {"display": "dfeed1", "hash": "0xD8A3CB65", "extern": null, "attributes": {}, "ids": ["iG7Q7Uzr"]}, "dryvol": {"display": "dryvol", "hash": "0xAEFD47D3", "extern": null, "attributes": {}, "ids": ["kJYDDgog"]}, "__hv_init": {"display": "__hv_init", "hash": "0xCE5CC65B", "extern": null, "attributes": {}, "ids": ["AJBhI0kX"]}, "Gain": {"display": "Gain", "hash": "0x677821DA", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}, "ids": ["eGza5oBR"]}, "Dry_Volume": {"display": "Dry_Volume", "hash": "0x5EFB46D2", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}, "ids": ["SIoX2X2C"]}, "Delay_Time": {"display": "Delay_Time", "hash": "0xEE78D101", "extern": "param", "attributes": {"min": 0.0, "max": 5000.0, "default": 500.0, "type": "float"}, "ids": ["UQL9KHRi"]}, "Wet_Volume": {"display": "Wet_Volume", "hash": "0xEA49B5A", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.75, "type": "float"}, "ids": ["ex2b75Oc"]}, "Delay_Feedback": {"display": "Delay_Feedback", "hash": "0x36377AA9", "extern": "param", "attributes": {"min": 0.0, "max": 1.0, "default": 0.25, "type": "float"}, "ids": ["LQwqfjMo"]}}, "sendMessage": [{"id": "4cpXTaBY", "onMessage": [[{"id": "wldZJDJM", "inletIndex": 0}]]}, {"id": "fRPZcVCt", "onMessage": [[{"id": "dFhbvIPr", "inletIndex": 0}]]}, {"id": "Om7ifKfd", "onMessage": [[{"id": "4A35hG1G", "inletIndex": 0}]]}, {"id": "LnHfGF8V", "onMessage": [[{"id": "eLw9NCKn", "inletIndex": 0}]]}, {"id": "mOyup5ir", "onMessage": [[{"id": "jiugLxbD", "inletIndex": 0}]]}, {"id": "hdrV3jKk", "onMessage": [[{"id": "B288dglo", "inletIndex": 0}]]}, {"id": "B288dglo", "onMessage": [[{"id": "NAf7xjjy", "inletIndex": 0}]]}, {"id": "OFD5iAqe", "onMessage": [[{"id": "EI4kKdvf", "inletIndex": 0}, {"id": "OFD5iAqe", "inletIndex": 0}, {"id": "kTfJPrs1", "inletIndex": 1}]]}, {"id": "EI4kKdvf", "onMessage": [[{"id": "o1Fk4mWr", "inletIndex": 0}]]}, {"id": "4MyuHNhK", "onMessage": [[{"id": "clwxczYs", "inletIndex": 0}]]}, {"id": "TFqOKFso", "onMessage": [[{"id": "DlDeZMFB", "inletIndex": 0}, {"id": "OFD5iAqe", "inletIndex": 2}, {"id": "g87DeymK", "inletIndex": 0}]]}, {"id": "clwxczYs", "onMessage": [[{"id": "TFqOKFso", "inletIndex": 0}]]}, {"id": "NAf7xjjy", "onMessage": [[{"id": "4MyuHNhK", "inletIndex": 0}]]}, {"id": "o1Fk4mWr", "onMessage": [[{"id": "TFqOKFso", "inletIndex": 0}]]}, {"id": "g87DeymK", "onMessage": [[{"id": "OFD5iAqe", "inletIndex": 0}]]}, {"id": "DlDeZMFB", "onMessage": [[{"id": "EI4kKdvf", "inletIndex": 2}]]}, {"id": "zhu6BFdy", "onMessage": [[{"id": "99kJjPmN", "inletIndex": 0}]]}, {"id": "c68iDIVD", "onMessage": [[{"id": "LlBI3RTT", "inletIndex": 0}]]}, {"id": "LlBI3RTT", "onMessage": [[{"id": "qp875S9R", "inletIndex": 0}]]}, {"id": "nONxuBGP", "onMessage": [[{"id": "2Y6gSDxh", "inletIndex": 0}]]}, {"id": "FTWI9Lbr", "onMessage": [[{"id": "4Y58Ksfn", "inletIndex": 0}, {"id": "OJVVdLuf", "inletIndex": 0}]]}, {"id": "4Y58Ksfn", "onMessage": [[{"id": "OJVVdLuf", "inletIndex": 0}, {"id": "4Y58Ksfn", "inletIndex": 0}]]}, {"id": "OJVVdLuf", "onMessage": [[], [{"id": "HsqyfMPx", "inletIndex": 0}]]}, {"id": "kd9uMvnd", "onMessage": [[{"id": "khIQbSXe", "inletIndex": 0}]]}, {"id": "qp875S9R", "onMessage": [[{"id": "kd9uMvnd", "inletIndex": 0}]]}, {"id": "99kJjPmN", "onMessage": [[{"id": "60hMCcpx", "inletIndex": 0}, {"id": "OJVVdLuf", "inletIndex": 0}, {"id": "zoAHkSwC", "inletIndex": 0}]]}, {"id": "kPqFOU4H", "onMessage": [[{"id": "HsqyfMPx", "inletIndex": 1}, {"id": "4Y58Ksfn", "inletIndex": 2}]]}, {"id": "2Y6gSDxh", "onMessage": [[{"id": "kPqFOU4H", "inletIndex": 0}]]}, {"id": "60hMCcpx", "onMessage": [[{"id": "FTWI9Lbr", "inletIndex": 0}, {"id": "4Y58Ksfn", "inletIndex": 0}]]}, {"id": "2y7SxePc", "onMessage": [[{"id": "khIQbSXe", "inletIndex": 1}]]}, {"id": "khIQbSXe", "onMessage": [[{"id": "99kJjPmN", "inletIndex": 1}]]}, {"id": "zoAHkSwC", "onMessage": [[{"id": "FTWI9Lbr", "inletIndex": 0}]]}, {"id": "H69XkGIL", "onMessage": [[{"id": "FTWI9Lbr", "inletIndex": 2}]]}, {"id": "HsqyfMPx", "onMessage": [[{"id": "H69XkGIL", "inletIndex": 0}]]}, {"id": "spzeS2Y3", "onMessage": [[{"id": "nONxuBGP", "inletIndex": 0}, {"id": "c68iDIVD", "inletIndex": 0}, {"id": "zhu6BFdy", "inletIndex": 0}]]}, {"id": "wldZJDJM", "onMessage": [[{"id": "kE9u8lIG", "inletIndex": 0}]], "extern": null, "hash": "0x37BBAC62", "display": "dtime1", "name": "dtime1"}, {"id": "dFhbvIPr", "onMessage": [[{"id": "pzAN0z6v", "inletIndex": 0}]], "extern": null, "hash": "0xCA9AE94A", "display": "dvol1", "name": "dvol1"}, {"id": "4A35hG1G", "onMessage": [[{"id": "iG7Q7Uzr", "inletIndex": 0}]], "extern": null, "hash": "0xD8A3CB65", "display": "dfeed1", "name": "dfeed1"}, {"id": "jiugLxbD", "onMessage": [[{"id": "kJYDDgog", "inletIndex": 0}]], "extern": null, "hash": "0xAEFD47D3", "display": "dryvol", "name": "dryvol"}, {"id": "kE9u8lIG", "onMessage": [[{"id": "kd9uMvnd", "inletIndex": 1}, {"id": "spzeS2Y3", "inletIndex": 0}]]}, {"id": "pzAN0z6v", "onMessage": [[{"id": "o7lB6W7R", "inletIndex": 0}]]}, {"id": "iG7Q7Uzr", "onMessage": [[{"id": "4RDRwlaL", "inletIndex": 0}]]}, {"id": "kJYDDgog", "onMessage": [[{"id": "vL8TEPN9", "inletIndex": 0}]]}, {"id": "AJBhI0kX", "onMessage": [[{"id": "hdrV3jKk", "inletIndex": 0}, {"id": "2y7SxePc", "inletIndex": 0}, {"id": "nONxuBGP", "inletIndex": 0}, {"id": "c68iDIVD", "inletIndex": 0}, {"id": "zhu6BFdy", "inletIndex": 0}]]}, {"id": "eGza5oBR", "onMessage": [[{"id": "LnHfGF8V", "inletIndex": 0}]]}, {"id": "SIoX2X2C", "onMessage": [[{"id": "mOyup5ir", "inletIndex": 0}]]}, {"id": "UQL9KHRi", "onMessage": [[{"id": "4cpXTaBY", "inletIndex": 0}]]}, {"id": "ex2b75Oc", "onMessage": [[{"id": "fRPZcVCt", "inletIndex": 0}]]}, {"id": "LQwqfjMo", "onMessage": [[{"id": "Om7ifKfd", "inletIndex": 0}]]}]}, "signal": {"numInputBuffers": 2, "numOutputBuffers": 2, "numTemporaryBuffers": {"float": 4, "integer": 0}, "processOrder": [{"id": "GGTMonx0", "inputBuffers": [{"type": "input", "index": 0}, {"type": "input", "index": 1}], "outputBuffers": [{"type": "~f>", "index": 0}]}, {"id": "38jlXfLk", "inputBuffers": [], "outputBuffers": [{"type": "~f>", "index": 1}]}, {"id": "CfeUbU9R", "inputBuffers": [{"type": "~f>", "index": 0}, {"type": "~f>", "index": 1}], "outputBuffers": [{"type": "~f>", "index": 1}]}, {"id": "OJVVdLuf", "inputBuffers": [], "outputBuffers": [{"type": "~f>", "index": 0}]}, {"id": "59xeVZ71", "inputBuffers": [], "outputBuffers": [{"type": "~f>", "index": 2}]}, {"id": "yUsjcNYR", "inputBuffers": [], "outputBuffers": [{"type": "~f>", "index": 3}]}, {"id": "XBUwwqqx", "inputBuffers": [{"type": "~f>", "index": 1}, {"type": "~f>", "index": 3}], "outputBuffers": [{"type": "~f>", "index": 3}]}, {"id": "DKIa3dd2", "inputBuffers": [{"type": "~f>", "index": 0}, {"type": "~f>", "index": 2}, {"type": "~f>", "index": 3}], "outputBuffers": [{"type": "~f>", "index": 3}]}, {"id": "1E66K3Ry", "inputBuffers": [], "outputBuffers": [{"type": "~f>", "index": 2}]}, {"id": "D6wsFINb", "inputBuffers": [{"type": "~f>", "index": 1}, {"type": "~f>", "index": 2}], "outputBuffers": [{"type": "~f>", "index": 2}]}, {"id": "kTfJPrs1", "inputBuffers": [{"type": "~f>", "index": 2}], "outputBuffers": []}, {"id": "QLbNvMZU", "inputBuffers": [], "outputBuffers": [{"type": "~f>", "index": 2}]}, {"id": "aZPZjXe3", "inputBuffers": [{"type": "~f>", "index": 0}, {"type": "~f>", "index": 2}], "outputBuffers": [{"type": "~f>", "index": 2}]}, {"id": "72seEPQr", "inputBuffers": [{"type": "~f>", "index": 2}], "outputBuffers": []}, {"id": "iIE4ePU1", "inputBuffers": [{"type": "~f>", "index": 3}, {"type": "output", "index": 0}], "outputBuffers": [{"type": "output", "index": 0}]}, {"id": "bbsF8nB6", "inputBuffers": [{"type": "~f>", "index": 3}, {"type": "output", "index": 1}], "outputBuffers": [{"type": "output", "index": 1}]}]}} \ No newline at end of file diff --git a/delay_simple/plugin/README.md b/delay_simple/plugin/README.md new file mode 100644 index 0000000..95dad59 --- /dev/null +++ b/delay_simple/plugin/README.md @@ -0,0 +1,27 @@ +# Distrho Plugin Format + +This output is for the Distrho Plugin Format ([DPF](https://github.com/DISTRHO/DPF)), and can be used to build LV2, VST2 and jack standalone versions of your Heavy code. + +# Build Instructions + +Make sure you have a (recent) DPF in the root of your output directory + +```bash +$ cd +$ git clone https://github.com/DISTRHO/DPF.git dpf +``` + +Then compile the plugins from the source folder: + +```bash +$ make +``` + +This will result in an `bin/` folder with all binary assets. + +* LV2 - move `bin/.lv2/` folder to your local `~/.lv2/` dir +* VST2 - move `bin/-vst.so`, can be placed directly into your `~/.vst/` dir + +## Jack + +The Jack binary can be executed in place and used to test functionality `./bin/`. Currently there is no UI, so this is not recommended. You will have to be running jack in order to use this. diff --git a/delay_simple/plugin/build.json b/delay_simple/plugin/build.json new file mode 100644 index 0000000..72ce655 --- /dev/null +++ b/delay_simple/plugin/build.json @@ -0,0 +1 @@ +{"linux": {"x64": {"args": [["-j"]], "projectDir": ["linux"], "binaryDir": ["build", "linux", "x64", "release"]}}} \ No newline at end of file diff --git a/delay_simple/plugin/source/DistrhoPluginInfo.h b/delay_simple/plugin/source/DistrhoPluginInfo.h new file mode 100644 index 0000000..6816074 --- /dev/null +++ b/delay_simple/plugin/source/DistrhoPluginInfo.h @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#define DISTRHO_PLUGIN_NAME "delay_simple" + +#define DISTRHO_PLUGIN_URI "http://wasted.audio/lv2/plugin/delay_simple" + +#define DISTRHO_PLUGIN_NUM_INPUTS 2 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 +#define DISTRHO_PLUGIN_IS_SYNTH 0 +#define DISTRHO_PLUGIN_HAS_UI 0 +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 +#define DISTRHO_PLUGIN_WANT_PROGRAMS 0 +#define DISTRHO_PLUGIN_WANT_STATE 0 +#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 +#define DISTRHO_PLUGIN_WANT_FULL_STATE 0 +#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 +#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 + +// for level monitoring +#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 \ No newline at end of file diff --git a/delay_simple/plugin/source/HeavyContext.cpp b/delay_simple/plugin/source/HeavyContext.cpp new file mode 100644 index 0000000..88fa134 --- /dev/null +++ b/delay_simple/plugin/source/HeavyContext.cpp @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HeavyContext.hpp" +#include "HvTable.h" + +void defaultSendHook(HeavyContextInterface *context, + const char *sendName, hv_uint32_t sendHash, const HvMessage *msg) { + HeavyContext *thisContext = reinterpret_cast(context); + const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage); + ReceiverMessagePair *p = reinterpret_cast(hLp_getWriteBuffer(&thisContext->outQueue, numBytes)); + if (p != nullptr) { + p->receiverHash = sendHash; + msg_copyToBuffer(msg, (char *) &p->msg, msg_getSize(msg)); + hLp_produce(&thisContext->outQueue, numBytes); + } else { + hv_assert(false && + "::defaultSendHook - The out message queue is full and cannot accept more messages until they " + "have been processed. Try increasing the outQueueKb size in the new_with_options() constructor."); + } +} + +HeavyContext::HeavyContext(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) : + sampleRate(sampleRate) { + + hv_assert(sampleRate > 0.0); // sample rate must be positive + hv_assert(poolKb > 0); + hv_assert(inQueueKb > 0); + hv_assert(outQueueKb >= 0); + + blockStartTimestamp = 0; + printHook = nullptr; + userData = nullptr; + + // if outQueueKb is positive, then the outQueue is allocated and the default sendhook is set. + // Otherwise outQueue and the sendhook are set to NULL. + sendHook = (outQueueKb > 0) ? &defaultSendHook : nullptr; + + HV_SPINLOCK_RELEASE(inQueueLock); + HV_SPINLOCK_RELEASE(outQueueLock); + + numBytes = sizeof(HeavyContext); + + numBytes += mq_initWithPoolSize(&mq, poolKb); + numBytes += hLp_init(&inQueue, inQueueKb * 1024); + numBytes += hLp_init(&outQueue, outQueueKb * 1024); // outQueueKb value of 0 sets everything to NULL +} + +HeavyContext::~HeavyContext() { + mq_free(&mq); + hLp_free(&inQueue); + hLp_free(&outQueue); +} + +bool HeavyContext::sendBangToReceiver(hv_uint32_t receiverHash) { + HvMessage *m = HV_MESSAGE_ON_STACK(1); + msg_initWithBang(m, 0); + bool success = sendMessageToReceiver(receiverHash, 0.0, m); + return success; +} + +bool HeavyContext::sendFloatToReceiver(hv_uint32_t receiverHash, float f) { + HvMessage *m = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(m, 0, f); + bool success = sendMessageToReceiver(receiverHash, 0.0, m); + return success; +} + +bool HeavyContext::sendSymbolToReceiver(hv_uint32_t receiverHash, const char *s) { + hv_assert(s != nullptr); + HvMessage *m = HV_MESSAGE_ON_STACK(1); + msg_initWithSymbol(m, 0, (char *) s); + bool success = sendMessageToReceiver(receiverHash, 0.0, m); + return success; +} + +bool HeavyContext::sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *format, ...) { + hv_assert(delayMs >= 0.0); + hv_assert(format != nullptr); + + va_list ap; + va_start(ap, format); + const int numElem = (int) hv_strlen(format); + HvMessage *m = HV_MESSAGE_ON_STACK(numElem); + msg_init(m, numElem, blockStartTimestamp + (hv_uint32_t) (hv_max_d(0.0, delayMs)*getSampleRate()/1000.0)); + for (int i = 0; i < numElem; i++) { + switch (format[i]) { + case 'b': msg_setBang(m, i); break; + case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; + case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; + case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; + default: break; + } + } + va_end(ap); + + bool success = sendMessageToReceiver(receiverHash, delayMs, m); + return success; +} + +bool HeavyContext::sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) { + hv_assert(delayMs >= 0.0); + hv_assert(m != nullptr); + + const hv_uint32_t timestamp = blockStartTimestamp + + (hv_uint32_t) (hv_max_d(0.0, delayMs)*(getSampleRate()/1000.0)); + + ReceiverMessagePair *p = nullptr; + HV_SPINLOCK_ACQUIRE(inQueueLock); + const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(m) - sizeof(HvMessage); + p = (ReceiverMessagePair *) hLp_getWriteBuffer(&inQueue, numBytes); + if (p != nullptr) { + p->receiverHash = receiverHash; + msg_copyToBuffer(m, (char *) &p->msg, msg_getSize(m)); + msg_setTimestamp(&p->msg, timestamp); + hLp_produce(&inQueue, numBytes); + } else { + hv_assert(false && + "::sendMessageToReceiver - The input message queue is full and cannot accept more messages until they " + "have been processed. Try increasing the inQueueKb size in the new_with_options() constructor."); + } + HV_SPINLOCK_RELEASE(inQueueLock); + return (p != nullptr); +} + +bool HeavyContext::cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + return mq_removeMessage(&mq, m, sendMessage); +} + +HvMessage *HeavyContext::scheduleMessageForObject(const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex) { + HvMessage *n = mq_addMessageByTimestamp(&mq, m, letIndex, sendMessage); + return n; +} + +float *HeavyContext::getBufferForTable(hv_uint32_t tableHash) { + HvTable *t = getTableForHash(tableHash); + if (t != nullptr) { + return hTable_getBuffer(t); + } else return nullptr; +} + +int HeavyContext::getLengthForTable(hv_uint32_t tableHash) { + HvTable *t = getTableForHash(tableHash); + if (t != nullptr) { + return hTable_getLength(t); + } else return 0; +} + +bool HeavyContext::setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) { + HvTable *t = getTableForHash(tableHash); + if (t != nullptr) { + hTable_resize(t, newSampleLength); + return true; + } else return false; +} + +void HeavyContext::lockAcquire() { + HV_SPINLOCK_ACQUIRE(inQueueLock); +} + +bool HeavyContext::lockTry() { + HV_SPINLOCK_TRY(inQueueLock); +} + +void HeavyContext::lockRelease() { + HV_SPINLOCK_RELEASE(inQueueLock); +} + +void HeavyContext::setInputMessageQueueSize(int inQueueKb) { + hv_assert(inQueueKb > 0); + hLp_free(&inQueue); + hLp_init(&inQueue, inQueueKb*1024); +} + +void HeavyContext::setOutputMessageQueueSize(int outQueueKb) { + hv_assert(outQueueKb > 0); + hLp_free(&outQueue); + hLp_init(&outQueue, outQueueKb*1024); +} + +bool HeavyContext::getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) { + *destinationHash = 0; + ReceiverMessagePair *p = nullptr; + hv_assert((sendHook == &defaultSendHook) && + "::getNextSentMessage - this function won't do anything if the msg outQueue " + "size is 0, or you've overriden the default sendhook."); + if (sendHook == &defaultSendHook) { + HV_SPINLOCK_ACQUIRE(outQueueLock); + if (hLp_hasData(&outQueue)) { + hv_uint32_t numBytes = 0; + p = reinterpret_cast(hLp_getReadBuffer(&outQueue, &numBytes)); + hv_assert((p != nullptr) && "::getNextSentMessage - something bad happened."); + hv_assert(numBytes >= sizeof(ReceiverMessagePair)); + hv_assert((numBytes <= msgLengthBytes) && + "::getNextSentMessage - the sent message is bigger than the message " + "passed to handle it."); + *destinationHash = p->receiverHash; + hv_memcpy(outMsg, &p->msg, numBytes); + hLp_consume(&outQueue); + } + HV_SPINLOCK_RELEASE(outQueueLock); + } + return (p != nullptr); +} + +hv_uint32_t HeavyContext::getHashForString(const char *str) { + return hv_string_to_hash(str); +} + +HvTable *_hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { + hv_assert(c != nullptr); + return reinterpret_cast(c)->getTableForHash(tableHash); +} + +void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { + hv_assert(c != nullptr); + reinterpret_cast(c)->scheduleMessageForReceiver(receiverHash, m); +} + +HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex) { + hv_assert(c != nullptr); + HvMessage *n = reinterpret_cast(c)->scheduleMessageForObject( + m, sendMessage, letIndex); + return n; +} + +#ifdef __cplusplus +extern "C" { +#endif + +HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash) { + return _hv_table_get(c, tableHash); +} + +void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) { + _hv_scheduleMessageForReceiver(c, receiverHash, m); +} + +HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex) { + return _hv_scheduleMessageForObject(c, m, sendMessage, letIndex); +} + +#ifdef __cplusplus +} +#endif diff --git a/delay_simple/plugin/source/HeavyContext.hpp b/delay_simple/plugin/source/HeavyContext.hpp new file mode 100644 index 0000000..87b95d7 --- /dev/null +++ b/delay_simple/plugin/source/HeavyContext.hpp @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTEXT_H_ +#define _HEAVY_CONTEXT_H_ + +#include "HeavyContextInterface.hpp" +#include "HvLightPipe.h" +#include "HvMessageQueue.h" +#include "HvMath.h" + +struct HvTable; + +class HeavyContext : public HeavyContextInterface { + + public: + HeavyContext(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); + virtual ~HeavyContext(); + + int getSize() override { return (int) numBytes; } + + double getSampleRate() override { return sampleRate; } + + hv_uint32_t getCurrentSample() override { return blockStartTimestamp; } + float samplesToMilliseconds(hv_uint32_t numSamples) override { return (float) (1000.0*numSamples/sampleRate); } + hv_uint32_t millisecondsToSamples(float ms) override { return (hv_uint32_t) (hv_max_f(0.0f,ms)*sampleRate/1000.0); } + + void setUserData(void *x) override { userData = x; } + void *getUserData() override { return userData; } + + // hook management + void setSendHook(HvSendHook_t *f) override { sendHook = f; } + HvSendHook_t *getSendHook() override { return sendHook; } + + void setPrintHook(HvPrintHook_t *f) override { printHook = f; } + HvPrintHook_t *getPrintHook() override { return printHook; } + + // message scheduling + bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) override; + bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) override; + bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) override; + bool sendBangToReceiver(hv_uint32_t receiverHash) override; + bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) override; + bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) override; + + // table manipulation + float *getBufferForTable(hv_uint32_t tableHash) override; + int getLengthForTable(hv_uint32_t tableHash) override; + bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) override; + + // lock control + void lockAcquire() override; + bool lockTry() override; + void lockRelease() override; + + // message queue management + void setInputMessageQueueSize(int inQueueKb) override; + void setOutputMessageQueueSize(int outQueueKb) override; + bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLength) override; + + // utility functions + static hv_uint32_t getHashForString(const char *str); + + protected: + virtual HvTable *getTableForHash(hv_uint32_t tableHash) = 0; + friend HvTable *_hv_table_get(HeavyContextInterface *, hv_uint32_t); + + virtual void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) = 0; + friend void _hv_scheduleMessageForReceiver(HeavyContextInterface *, hv_uint32_t, HvMessage *); + + HvMessage *scheduleMessageForObject(const HvMessage *, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int); + friend HvMessage *_hv_scheduleMessageForObject(HeavyContextInterface *, const HvMessage *, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int); + + friend void defaultSendHook(HeavyContextInterface *, const char *, hv_uint32_t, const HvMessage *); + + // object state + double sampleRate; + hv_uint32_t blockStartTimestamp; + hv_size_t numBytes; + HvMessageQueue mq; + HvSendHook_t *sendHook; + HvPrintHook_t *printHook; + void *userData; + HvLightPipe inQueue; + HvLightPipe outQueue; + hv_atomic_bool inQueueLock; + hv_atomic_bool outQueueLock; +}; + +#endif // _HEAVY_CONTEXT_H_ diff --git a/delay_simple/plugin/source/HeavyContextInterface.hpp b/delay_simple/plugin/source/HeavyContextInterface.hpp new file mode 100644 index 0000000..a78fcbf --- /dev/null +++ b/delay_simple/plugin/source/HeavyContextInterface.hpp @@ -0,0 +1,291 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTEXT_INTERFACE_H_ +#define _HEAVY_CONTEXT_INTERFACE_H_ + +#include "HvUtils.h" + +#ifndef _HEAVY_DECLARATIONS_ +#define _HEAVY_DECLARATIONS_ + +class HeavyContextInterface; +struct HvMessage; + +typedef enum { + HV_PARAM_TYPE_PARAMETER_IN, + HV_PARAM_TYPE_PARAMETER_OUT, + HV_PARAM_TYPE_EVENT_IN, + HV_PARAM_TYPE_EVENT_OUT +} HvParameterType; + +typedef struct HvParameterInfo { + const char *name; // the human readable parameter name + hv_uint32_t hash; // an integer identified used by heavy for this parameter + HvParameterType type; // type of this parameter + float minVal; // the minimum value of this parameter + float maxVal; // the maximum value of this parameter + float defaultVal; // the default value of this parameter +} HvParameterInfo; + +typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); +typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); + +#endif // _HEAVY_DECLARATIONS_ + + + +class HeavyContextInterface { + + public: + HeavyContextInterface() {} + virtual ~HeavyContextInterface() {}; + + /** Returns the read-only user-assigned name of this patch. */ + virtual const char *getName() = 0; + + /** Returns the number of input channels with which this context has been configured. */ + virtual int getNumInputChannels() = 0; + + /** Returns the number of output channels with which this context has been configured. */ + virtual int getNumOutputChannels() = 0; + + /** + * Returns the total size in bytes of the context. + * This value may change if tables are resized. + */ + virtual int getSize() = 0; + + /** Returns the sample rate with which this context has been configured. */ + virtual double getSampleRate() = 0; + + /** Returns the current patch time in samples. This value is always exact. */ + virtual hv_uint32_t getCurrentSample() = 0; + virtual float samplesToMilliseconds(hv_uint32_t numSamples) = 0; + + /** Converts milliseconds to samples. Input is limited to non-negative range. */ + virtual hv_uint32_t millisecondsToSamples(float ms) = 0; + + /** Sets a user-definable value. This value is never manipulated by Heavy. */ + virtual void setUserData(void *x) = 0; + + /** Returns the user-defined data. */ + virtual void *getUserData() = 0; + + /** + * Set the send hook. The function is called whenever a message is sent to any send object. + * Messages returned by this function should NEVER be freed. If the message must persist, call + * hv_msg_copy() first. + */ + virtual void setSendHook(HvSendHook_t *f) = 0; + + /** Returns the send hook, or NULL if unset. */ + virtual HvSendHook_t *getSendHook() = 0; + + /** Set the print hook. The function is called whenever a message is sent to a print object. */ + virtual void setPrintHook(HvPrintHook_t *f) = 0; + + /** Returns the print hook, or NULL if unset. */ + virtual HvPrintHook_t *getPrintHook() = 0; + + /** + * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [[LLLL][RRRR]] + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ + virtual int process(float **inputBuffers, float **outputBuffer, int n) = 0; + + /** + * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LLLLRRRR] + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ + virtual int processInline(float *inputBuffers, float *outputBuffer, int n) = 0; + + /** + * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LRLRLRLR] + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ + virtual int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) = 0; + + /** + * Sends a formatted message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendMessageToReceiver(hv_uint32_t receiverHash, double delayMs, HvMessage *m) = 0; + + /** + * Sends a formatted message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendMessageToReceiverV(hv_uint32_t receiverHash, double delayMs, const char *fmt, ...) = 0; + + /** + * A convenience function to send a float to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendFloatToReceiver(hv_uint32_t receiverHash, float f) = 0; + + /** + * A convenience function to send a bang to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendBangToReceiver(hv_uint32_t receiverHash) = 0; + + /** + * A convenience function to send a symbol to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ + virtual bool sendSymbolToReceiver(hv_uint32_t receiverHash, const char *symbol) = 0; + + /** + * Cancels a previously scheduled message. + * + * @param sendMessage May be NULL. + */ + virtual bool cancelMessage(HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)=nullptr) = 0; + + /** + * Returns information about each parameter such as name, hash, and range. + * The total number of parameters is always returned. + * + * @param index The parameter index. + * @param info A pointer to a HvParameterInfo struct. May be null. + * + * @return The total number of parameters. + */ + virtual int getParameterInfo(int index, HvParameterInfo *info) = 0; + + /** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ + virtual float *getBufferForTable(hv_uint32_t tableHash) = 0; + + /** Returns the length of this table in samples. */ + virtual int getLengthForTable(hv_uint32_t tableHash) = 0; + + /** + * Resizes the table to the given length. + * + * Existing contents are copied to the new table. Remaining space is cleared + * if the table is longer than the original, truncated otherwise. + * + * @param tableHash The table identifier. + * @param newSampleLength The new length of the table, in samples. + * + * @return False if the table could not be found. True otherwise. + */ + virtual bool setLengthForTable(hv_uint32_t tableHash, hv_uint32_t newSampleLength) = 0; + + /** + * Acquire the input message queue lock. + * + * This function will block until the message lock as been acquired. + * Typical applications will not require the use of this function. + */ + virtual void lockAcquire() = 0; + + /** + * Try to acquire the input message queue lock. + * + * If the lock has been acquired, hv_lock_release() must be called to release it. + * Typical applications will not require the use of this function. + * + * @return Returns true if the lock has been acquired, false otherwise. + */ + virtual bool lockTry() = 0; + + /** + * Release the input message queue lock. + * + * Typical applications will not require the use of this function. + */ + virtual void lockRelease() = 0; + + /** + * Set the size of the input message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * + * @param inQueueKb Must be positive i.e. at least one. + */ + virtual void setInputMessageQueueSize(int inQueueKb) = 0; + + /** + * Set the size of the output message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * Only the default sendhook uses the outgoing message queue. If the default + * sendhook is not being used, then this function is not useful. + * + * @param outQueueKb Must be postive i.e. at least one. + */ + virtual void setOutputMessageQueueSize(int outQueueKb) = 0; + + /** + * Get the next message in the outgoing queue, will also consume the message. + * Returns false if there are no messages. + * + * @param destinationHash a hash of the name of the receiver the message was sent to. + * @param outMsg message pointer that is filled by the next message contents. + * @param msgLengthBytes max length of outMsg in bytes. + * + * @return True if there is a message in the outgoing queue. + */ + virtual bool getNextSentMessage(hv_uint32_t *destinationHash, HvMessage *outMsg, hv_size_t msgLengthBytes) = 0; + + /** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ + static hv_uint32_t getHashForString(const char *str); +}; + +#endif // _HEAVY_CONTEXT_INTERFACE_H_ diff --git a/delay_simple/plugin/source/HeavyDPF_delay_simple.cpp b/delay_simple/plugin/source/HeavyDPF_delay_simple.cpp new file mode 100644 index 0000000..5fb3ff3 --- /dev/null +++ b/delay_simple/plugin/source/HeavyDPF_delay_simple.cpp @@ -0,0 +1,568 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "Heavy_delay_simple.h" +#include "HeavyDPF_delay_simple.hpp" +#include + + +#define HV_LV2_NUM_PARAMETERS 5 + +#define HV_HASH_NOTEIN 0x67E37CA3 +#define HV_HASH_CTLIN 0x41BE0f9C +#define HV_HASH_PGMIN 0x2E1EA03D +#define HV_HASH_TOUCHIN 0x553925BD +#define HV_HASH_BENDIN 0x3083F0F7 +#define HV_HASH_MIDIIN 0x149631bE +#define HV_HASH_MIDIREALTIMEIN 0x6FFF0BCF + +#define HV_HASH_NOTEOUT 0xD1D4AC2 +#define HV_HASH_CTLOUT 0xE5e2A040 +#define HV_HASH_PGMOUT 0x8753E39E +#define HV_HASH_TOUCHOUT 0x476D4387 +#define HV_HASH_BENDOUT 0xE8458013 +#define HV_HASH_MIDIOUT 0x6511DE55 +#define HV_HASH_MIDIOUTPORT 0x165707E4 + +#define MIDI_RT_CLOCK 0xF8 +#define MIDI_RT_START 0xFA +#define MIDI_RT_CONTINUE 0xFB +#define MIDI_RT_STOP 0xFC +#define MIDI_RT_ACTIVESENSE 0xFE +#define MIDI_RT_RESET 0xFF + +// midi realtime messages +std::set mrtSet { + MIDI_RT_CLOCK, + MIDI_RT_START, + MIDI_RT_CONTINUE, + MIDI_RT_STOP, + MIDI_RT_RESET +}; + + +START_NAMESPACE_DISTRHO + + +// ------------------------------------------------------------------- +// Heavy Send and Print hooks + +static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint32_t sendHash, const HvMessage *m) +{ + HeavyDPF_delay_simple* plugin = (HeavyDPF_delay_simple*)c->getUserData(); + if (plugin != nullptr) + { +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + plugin->handleMidiSend(sendHash, m); +#endif + } +} + +static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, const char *msgString, const HvMessage *m) +{ + char buf[64]; + char* dst = buf; + int len = strnlen(printLabel, 48); + dst = strncpy(dst, printLabel, len); + dst = strcpy(dst, " "); + dst = strncpy(dst, msgString, 63-len); + printf("> %s \n", buf); +} + +// ------------------------------------------------------------------- +// Main DPF plugin class + +HeavyDPF_delay_simple::HeavyDPF_delay_simple() + : Plugin(HV_LV2_NUM_PARAMETERS, 0, 0) +{ + + _parameters[0] = 0.25f; + + _parameters[1] = 500.0f; + + _parameters[2] = 0.75f; + + _parameters[3] = 0.75f; + + _parameters[4] = 0.75f; + + + _context = hv_delay_simple_new_with_options(getSampleRate(), 10, 5, 2); + _context->setUserData(this); + _context->setSendHook(&hvSendHookFunc); + _context->setPrintHook(&hvPrintHookFunc); + + + // ensure that the new context has the current parameters + for (int i = 0; i < HV_LV2_NUM_PARAMETERS; ++i) { + setParameterValue(i, _parameters[i]); + } + +} + +HeavyDPF_delay_simple::~HeavyDPF_delay_simple() { + hv_delay_simple_free(_context); +} + +void HeavyDPF_delay_simple::initParameter(uint32_t index, Parameter& parameter) +{ + + // initialise parameters with defaults + switch (index) + { + + case paramDelay_Feedback: + parameter.name = "Delay Feedback"; + parameter.symbol = "delay_feedback"; + parameter.hints = kParameterIsAutomable + ; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 0.25f; + break; + + case paramDelay_Time: + parameter.name = "Delay Time"; + parameter.symbol = "delay_time"; + parameter.hints = kParameterIsAutomable + ; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 5000.0f; + parameter.ranges.def = 500.0f; + break; + + case paramDry_Volume: + parameter.name = "Dry Volume"; + parameter.symbol = "dry_volume"; + parameter.hints = kParameterIsAutomable + ; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 0.75f; + break; + + case paramGain: + parameter.name = "Gain"; + parameter.symbol = "gain"; + parameter.hints = kParameterIsAutomable + ; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 0.75f; + break; + + case paramWet_Volume: + parameter.name = "Wet Volume"; + parameter.symbol = "wet_volume"; + parameter.hints = kParameterIsAutomable + ; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 1.0f; + parameter.ranges.def = 0.75f; + break; + + } + +} + +// ------------------------------------------------------------------- +// Internal data + +float HeavyDPF_delay_simple::getParameterValue(uint32_t index) const +{ + + return _parameters[index]; + +} + +void HeavyDPF_delay_simple::setParameterValue(uint32_t index, float value) +{ + + switch (index) { + + case 0: { + _context->sendFloatToReceiver( + Heavy_delay_simple::Parameter::In::DELAY_FEEDBACK, + value); + break; + } + + case 1: { + _context->sendFloatToReceiver( + Heavy_delay_simple::Parameter::In::DELAY_TIME, + value); + break; + } + + case 2: { + _context->sendFloatToReceiver( + Heavy_delay_simple::Parameter::In::DRY_VOLUME, + value); + break; + } + + case 3: { + _context->sendFloatToReceiver( + Heavy_delay_simple::Parameter::In::GAIN, + value); + break; + } + + case 4: { + _context->sendFloatToReceiver( + Heavy_delay_simple::Parameter::In::WET_VOLUME, + value); + break; + } + + default: return; + } + _parameters[index] = value; + +} + + +// ------------------------------------------------------------------- +// Process + +// void HeavyDPF_delay_simple::activate() +// { + +// } + +// void HeavyDPF_delay_simple::deactivate() +// { + +// } + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT +// ------------------------------------------------------------------- +// Midi Input handler + +void HeavyDPF_delay_simple::handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) +{ + // Realtime events + const TimePosition& timePos(getTimePosition()); + bool reset = false; + + if (timePos.playing) + { + if (timePos.frame == 0) + { + _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, + "ff", (float) MIDI_RT_RESET); + reset = true; + } + + if (! this->wasPlaying) + { + if (timePos.frame == 0) + { + _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, + "ff", (float) MIDI_RT_START); + } + if (! reset) + { + _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, + "ff", (float) MIDI_RT_CONTINUE); + } + } + } + else if (this->wasPlaying) + { + _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 0, + "ff", (float) MIDI_RT_STOP); + } + this->wasPlaying = timePos.playing; + + // sending clock ticks + if (timePos.playing && timePos.bbt.valid) + { + float samplesPerBeat = 60 * getSampleRate() / timePos.bbt.beatsPerMinute; + float samplesPerTick = samplesPerBeat / 24.0; + + /* get state */ + double nextClockTick = this->nextClockTick; + double sampleAtCycleStart = this->sampleAtCycleStart; + double sampleAtCycleEnd = sampleAtCycleStart + frames; + + while (nextClockTick < sampleAtCycleEnd) { + _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 1000*(nextClockTick - sampleAtCycleStart)/getSampleRate(), + "ff", (float) MIDI_RT_CLOCK); + nextClockTick += samplesPerTick; + } + + /* save variables for next cycle */ + this->sampleAtCycleStart = sampleAtCycleEnd; + this->nextClockTick = nextClockTick; + } + + // Midi events + for (uint32_t i=0; i < midiEventCount; ++i) + { + int status = midiEvents[i].data[0]; + int command = status & 0xF0; + int channel = status & 0x0F; + int data1 = midiEvents[i].data[1]; + int data2 = midiEvents[i].data[2]; + + // raw [midiin] messages + int dataSize = *(&midiEvents[i].data + 1) - midiEvents[i].data; + + for (int i = 0; i < dataSize; ++i) { + _context->sendMessageToReceiverV(HV_HASH_MIDIIN, 1000.0*timePos.frame/getSampleRate(), "ff", + (float) midiEvents[i].data[i], + (float) channel); + } + + if(mrtSet.find(status) != mrtSet.end()) + { + _context->sendMessageToReceiverV(HV_HASH_MIDIREALTIMEIN, 1000.0*timePos.frame/getSampleRate(), + "ff", (float) status); + } + + // typical midi messages + switch (command) { + case 0x80: { // note off + _context->sendMessageToReceiverV(HV_HASH_NOTEIN, 1000.0*timePos.frame/getSampleRate(), "fff", + (float) data1, // pitch + (float) 0, // velocity + (float) channel); + break; + } + case 0x90: { // note on + _context->sendMessageToReceiverV(HV_HASH_NOTEIN, 1000.0*timePos.frame/getSampleRate(), "fff", + (float) data1, // pitch + (float) data2, // velocity + (float) channel); + break; + } + case 0xB0: { // control change + _context->sendMessageToReceiverV(HV_HASH_CTLIN, 1000.0*timePos.frame/getSampleRate(), "fff", + (float) data2, // value + (float) data1, // cc number + (float) channel); + break; + } + case 0xC0: { // program change + _context->sendMessageToReceiverV(HV_HASH_PGMIN, 1000.0*timePos.frame/getSampleRate(), "ff", + (float) data1, + (float) channel); + break; + } + case 0xD0: { // aftertouch + _context->sendMessageToReceiverV(HV_HASH_TOUCHIN, 1000.0*timePos.frame/getSampleRate(), "ff", + (float) data1, + (float) channel); + break; + } + case 0xE0: { // pitch bend + // combine 7bit lsb and msb into 32bit int + hv_uint32_t value = (((hv_uint32_t) data2) << 7) | ((hv_uint32_t) data1); + _context->sendMessageToReceiverV(HV_HASH_BENDIN, 1000.0*timePos.frame/getSampleRate(), "ff", + (float) value, + (float) channel); + break; + } + default: break; + } + } +} +#endif + +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +// ------------------------------------------------------------------- +// Midi Send handler + +void HeavyDPF_delay_simple::handleMidiSend(uint32_t sendHash, const HvMessage *m) +{ + MidiEvent midiSendEvent; + midiSendEvent.frame = 0; + midiSendEvent.dataExt = nullptr; + + switch(sendHash){ + case HV_HASH_NOTEOUT: // __hv_noteout + { + uint8_t note = hv_msg_getFloat(m, 0); + uint8_t velocity = hv_msg_getFloat(m, 1); + uint8_t ch = hv_msg_getFloat(m, 2); + ch %= 16; // drop any pd "ports" + + midiSendEvent.size = 3; + if (velocity > 0){ + midiSendEvent.data[0] = 0x90 | ch; // noteon + } else { + midiSendEvent.data[0] = 0x80 | ch; // noteoff + } + midiSendEvent.data[1] = note; + midiSendEvent.data[2] = velocity; + + writeMidiEvent(midiSendEvent); + break; + } + case HV_HASH_CTLOUT: + { + uint8_t value = hv_msg_getFloat(m, 0); + uint8_t cc = hv_msg_getFloat(m, 1); + uint8_t ch = hv_msg_getFloat(m, 2); + ch %= 16; + + midiSendEvent.size = 3; + midiSendEvent.data[0] = 0xB0 | ch; // send CC + midiSendEvent.data[1] = cc; + midiSendEvent.data[2] = value; + + writeMidiEvent(midiSendEvent); + break; + } + case HV_HASH_PGMOUT: + { + uint8_t pgm = hv_msg_getFloat(m, 0); + uint8_t ch = hv_msg_getFloat(m, 1); + ch %= 16; + + midiSendEvent.size = 2; + midiSendEvent.data[0] = 0xC0 | ch; // send Program Change + midiSendEvent.data[1] = pgm; + + writeMidiEvent(midiSendEvent); + break; + } + case HV_HASH_TOUCHOUT: + { + uint8_t value = hv_msg_getFloat(m, 0); + uint8_t ch = hv_msg_getFloat(m, 1); + ch %= 16; + + midiSendEvent.size = 2; + midiSendEvent.data[0] = 0xD0 | ch; // send Touch + midiSendEvent.data[1] = value; + + writeMidiEvent(midiSendEvent); + break; + } + case HV_HASH_BENDOUT: + { + uint16_t value = hv_msg_getFloat(m, 0); + uint8_t lsb = value & 0x7F; + uint8_t msb = (value >> 7) & 0x7F; + uint8_t ch = hv_msg_getFloat(m, 1); + ch %= 16; + + midiSendEvent.size = 3; + midiSendEvent.data[0] = 0xE0 | ch; // send Bend + midiSendEvent.data[1] = lsb; + midiSendEvent.data[2] = msb; + + writeMidiEvent(midiSendEvent); + break; + } + case HV_HASH_MIDIOUT: // __hv_midiout + { + const uint8_t numElements = m->numElements; + if (numElements <=4 ) + { + for (int i = 0; i < numElements; ++i) + { + midiSendEvent.data[i] = hv_msg_getFloat(m, i); + } + } + else + { + printf("> we do not support sysex yet \n"); + break; + } + + // unsigned char* rawData = new unsigned char; + // for (int i = 0; i < numElements; ++i) { + // rawData[i] = (uint8_t) hv_msg_getFloat(m, i); + // printf("> data: %d \n", rawData[i]); + // } + + midiSendEvent.size = numElements; + // midiSendEvent.dataExt = (const uint8_t *) rawData; + + writeMidiEvent(midiSendEvent); + break; + } + default: + break; + } +} +#endif + +// ------------------------------------------------------------------- +// DPF Plugin run() loop + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT +void HeavyDPF_delay_simple::run(const float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) +{ + handleMidiInput(frames, midiEvents, midiEventCount); +#else +void HeavyDPF_delay_simple::run(const float** inputs, float** outputs, uint32_t frames) +{ +#endif + _context->process((float**)inputs, outputs, frames); +} + +// ------------------------------------------------------------------- +// Callbacks + +void HeavyDPF_delay_simple::sampleRateChanged(double newSampleRate) +{ + hv_delay_simple_free(_context); + + _context = hv_delay_simple_new_with_options(getSampleRate(), 10, 5, 2); + _context->setUserData(this); + _context->setSendHook(&hvSendHookFunc); + _context->setPrintHook(&hvPrintHookFunc); + + + // ensure that the new context has the current parameters + for (int i = 0; i < HV_LV2_NUM_PARAMETERS; ++i) { + setParameterValue(i, _parameters[i]); + } + +} + + +// ----------------------------------------------------------------------- +/* Plugin entry point, called by DPF to create a new plugin instance. */ + +Plugin* createPlugin() +{ + return new HeavyDPF_delay_simple(); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO \ No newline at end of file diff --git a/delay_simple/plugin/source/HeavyDPF_delay_simple.hpp b/delay_simple/plugin/source/HeavyDPF_delay_simple.hpp new file mode 100644 index 0000000..4d4638a --- /dev/null +++ b/delay_simple/plugin/source/HeavyDPF_delay_simple.hpp @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HEAVY_LV2_DELAY_SIMPLE_ +#define _HEAVY_LV2_DELAY_SIMPLE_ + +#include "DistrhoPlugin.hpp" +#include "DistrhoPluginInfo.h" +#include "Heavy_delay_simple.hpp" + +START_NAMESPACE_DISTRHO + +static void hvSendHookFunc(HeavyContextInterface *c, const char *sendName, uint32_t sendHash, const HvMessage *m); +static void hvPrintHookFunc(HeavyContextInterface *c, const char *printLabel, const char *msgString, const HvMessage *m); + +class HeavyDPF_delay_simple : public Plugin +{ +public: + enum Parameters + { + + paramDelay_Feedback, + + paramDelay_Time, + + paramDry_Volume, + + paramGain, + + paramWet_Volume, + + }; + + HeavyDPF_delay_simple(); + ~HeavyDPF_delay_simple() override; + + void handleMidiInput(uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount); + void handleMidiSend(uint32_t sendHash, const HvMessage *m); + +protected: + // ------------------------------------------------------------------- + // Information + + const char* getLabel() const noexcept override + { + return "delay_simple"; + } + + + const char* getDescription() const override + { + return "Simple Delay"; + } + + + const char* getMaker() const noexcept override + { + + return "Wasted Audio"; + + } + + + + const char* getLicense() const noexcept override + { + + return "ISC"; + + } + + uint32_t getVersion() const noexcept override + { + + return d_version(0, 0, 1); + + } + + int64_t getUniqueId() const noexcept override + { + return int64_t( 0x9D74795E ); + } + + // ------------------------------------------------------------------- + // Init + + void initParameter(uint32_t index, Parameter& parameter) override; + + // ------------------------------------------------------------------- + // Internal data + + float getParameterValue(uint32_t index) const override; + void setParameterValue(uint32_t index, float value) override; + + // ------------------------------------------------------------------- + // Process + + // void activate() override; + // void deactivate() override; + +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + void run(const float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) override; +#else + void run(const float** inputs, float** outputs, uint32_t frames) override; +#endif + + // ------------------------------------------------------------------- + // Callbacks + + void sampleRateChanged(double newSampleRate) override; + + // ------------------------------------------------------------------- + +private: + + // parameters + float _parameters[5]; // in range of [0,1] + + + // transport values + bool wasPlaying; + float samplesProcessed; + double nextClockTick; + double sampleAtCycleStart; + + // heavy context + HeavyContextInterface *_context; + + // HeavyDPF_delay_simple fdelay_simple; + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(HeavyDPF_delay_simple) +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // _HEAVY_LV2_DELAY_SIMPLE_ diff --git a/delay_simple/plugin/source/Heavy_delay_simple.cpp b/delay_simple/plugin/source/Heavy_delay_simple.cpp new file mode 100644 index 0000000..4191593 --- /dev/null +++ b/delay_simple/plugin/source/Heavy_delay_simple.cpp @@ -0,0 +1,659 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "Heavy_delay_simple.hpp" + +#include + +#define Context(_c) static_cast(_c) + + +/* + * C Functions + */ + +extern "C" { + HV_EXPORT HeavyContextInterface *hv_delay_simple_new(double sampleRate) { + // allocate aligned memory + void *ptr = hv_malloc(sizeof(Heavy_delay_simple)); + // ensure non-null + if (!ptr) return nullptr; + // call constructor + new(ptr) Heavy_delay_simple(sampleRate); + return Context(ptr); + } + + HV_EXPORT HeavyContextInterface *hv_delay_simple_new_with_options(double sampleRate, + int poolKb, int inQueueKb, int outQueueKb) { + // allocate aligned memory + void *ptr = hv_malloc(sizeof(Heavy_delay_simple)); + // ensure non-null + if (!ptr) return nullptr; + // call constructor + new(ptr) Heavy_delay_simple(sampleRate, poolKb, inQueueKb, outQueueKb); + return Context(ptr); + } + + HV_EXPORT void hv_delay_simple_free(HeavyContextInterface *instance) { + // call destructor + Context(instance)->~Heavy_delay_simple(); + // free memory + hv_free(instance); + } +} // extern "C" + + + + + + + +/* + * Class Functions + */ + +Heavy_delay_simple::Heavy_delay_simple(double sampleRate, int poolKb, int inQueueKb, int outQueueKb) + : HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) { + numBytes += sTabread_init(&sTabread_OJVVdLuf, &hTable_TFqOKFso, true); + numBytes += sTabwrite_init(&sTabwrite_kTfJPrs1, &hTable_TFqOKFso); + numBytes += cVar_init_f(&cVar_4cpXTaBY, 0.0f); + numBytes += cVar_init_f(&cVar_fRPZcVCt, 0.0f); + numBytes += cVar_init_f(&cVar_Om7ifKfd, 0.0f); + numBytes += cVar_init_f(&cVar_LnHfGF8V, 0.0f); + numBytes += cVar_init_f(&cVar_mOyup5ir, 0.0f); + numBytes += cDelay_init(this, &cDelay_OFD5iAqe, 0.0f); + numBytes += cDelay_init(this, &cDelay_EI4kKdvf, 0.0f); + numBytes += hTable_init(&hTable_TFqOKFso, 256); + numBytes += cTabhead_init(&cTabhead_zhu6BFdy, &hTable_TFqOKFso); + numBytes += cVar_init_s(&cVar_nONxuBGP, "del-delay1012"); + numBytes += cDelay_init(this, &cDelay_FTWI9Lbr, 0.0f); + numBytes += cDelay_init(this, &cDelay_4Y58Ksfn, 0.0f); + numBytes += cBinop_init(&cBinop_kd9uMvnd, 0.0f); // __mul + numBytes += cBinop_init(&cBinop_99kJjPmN, 0.0f); // __sub + numBytes += cBinop_init(&cBinop_khIQbSXe, 0.0f); // __max + numBytes += cBinop_init(&cBinop_HsqyfMPx, 0.0f); // __sub + numBytes += sVarf_init(&sVarf_DXN3F4mN, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_o7lB6W7R, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_4RDRwlaL, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_vL8TEPN9, 0.0f, 0.0f, false); + numBytes += sVarf_init(&sVarf_eLw9NCKn, 0.0f, 0.0f, false); + + // schedule a message to trigger all loadbangs via the __hv_init receiver + scheduleMessageForReceiver(0xCE5CC65B, msg_initWithBang(HV_MESSAGE_ON_STACK(1), 0)); +} + +Heavy_delay_simple::~Heavy_delay_simple() { + hTable_free(&hTable_TFqOKFso); +} + +HvTable *Heavy_delay_simple::getTableForHash(hv_uint32_t tableHash) {switch (tableHash) { + case 0x4678FF81: return &hTable_TFqOKFso; // del-delay1012 + default: return nullptr; + } +} + +void Heavy_delay_simple::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) { + switch (receiverHash) { + case 0x36377AA9: { // Delay_Feedback + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_LQwqfjMo_sendMessage); + break; + } + case 0xEE78D101: { // Delay_Time + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_UQL9KHRi_sendMessage); + break; + } + case 0x5EFB46D2: { // Dry_Volume + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_SIoX2X2C_sendMessage); + break; + } + case 0x677821DA: { // Gain + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_eGza5oBR_sendMessage); + break; + } + case 0xEA49B5A: { // Wet_Volume + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_ex2b75Oc_sendMessage); + break; + } + case 0xCE5CC65B: { // __hv_init + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_AJBhI0kX_sendMessage); + break; + } + case 0xD8A3CB65: { // dfeed1 + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_iG7Q7Uzr_sendMessage); + break; + } + case 0xAEFD47D3: { // dryvol + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_kJYDDgog_sendMessage); + break; + } + case 0x37BBAC62: { // dtime1 + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_kE9u8lIG_sendMessage); + break; + } + case 0xCA9AE94A: { // dvol1 + mq_addMessageByTimestamp(&mq, m, 0, &cReceive_pzAN0z6v_sendMessage); + break; + } + default: return; + } +} + +int Heavy_delay_simple::getParameterInfo(int index, HvParameterInfo *info) { + if (info != nullptr) { + switch (index) { + case 0: { + info->name = "Delay_Feedback"; + info->hash = 0x36377AA9; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.25f; + break; + } + case 1: { + info->name = "Delay_Time"; + info->hash = 0xEE78D101; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 5000.0f; + info->defaultVal = 500.0f; + break; + } + case 2: { + info->name = "Dry_Volume"; + info->hash = 0x5EFB46D2; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.75f; + break; + } + case 3: { + info->name = "Gain"; + info->hash = 0x677821DA; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.75f; + break; + } + case 4: { + info->name = "Wet_Volume"; + info->hash = 0xEA49B5A; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 1.0f; + info->defaultVal = 0.75f; + break; + } + default: { + info->name = "invalid parameter index"; + info->hash = 0; + info->type = HvParameterType::HV_PARAM_TYPE_PARAMETER_IN; + info->minVal = 0.0f; + info->maxVal = 0.0f; + info->defaultVal = 0.0f; + break; + } + } + } + return 5; +} + + + +/* + * Send Function Implementations + */ + + +void Heavy_delay_simple::cVar_4cpXTaBY_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_wldZJDJM_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cVar_fRPZcVCt_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_dFhbvIPr_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cVar_Om7ifKfd_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_4A35hG1G_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cVar_LnHfGF8V_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_eLw9NCKn, m); +} + +void Heavy_delay_simple::cVar_mOyup5ir_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cSend_jiugLxbD_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cMsg_hdrV3jKk_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "samplerate"); + cSystem_onMessage(_c, NULL, 0, m, &cSystem_B288dglo_sendMessage); +} + +void Heavy_delay_simple::cSystem_B288dglo_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_DIVIDE, 1000.0f, 0, m, &cBinop_NAf7xjjy_sendMessage); +} + +void Heavy_delay_simple::cDelay_OFD5iAqe_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_OFD5iAqe, m); + cDelay_onMessage(_c, &Context(_c)->cDelay_EI4kKdvf, 0, m, &cDelay_EI4kKdvf_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_OFD5iAqe, 0, m, &cDelay_OFD5iAqe_sendMessage); + sTabwrite_onMessage(_c, &Context(_c)->sTabwrite_kTfJPrs1, 1, m, NULL); +} + +void Heavy_delay_simple::cDelay_EI4kKdvf_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_EI4kKdvf, m); + cMsg_o1Fk4mWr_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cBinop_4MyuHNhK_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_clwxczYs_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::hTable_TFqOKFso_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_DlDeZMFB_sendMessage(_c, 0, m); + cDelay_onMessage(_c, &Context(_c)->cDelay_OFD5iAqe, 2, m, &cDelay_OFD5iAqe_sendMessage); + cCast_onMessage(_c, HV_CAST_BANG, 0, m, &cCast_g87DeymK_sendMessage); +} + +void Heavy_delay_simple::cMsg_clwxczYs_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(2); + msg_init(m, 2, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "resize"); + msg_setElementToFrom(m, 1, n, 0); + hTable_onMessage(_c, &Context(_c)->hTable_TFqOKFso, 0, m, &hTable_TFqOKFso_sendMessage); +} + +void Heavy_delay_simple::cBinop_NAf7xjjy_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_MULTIPLY, 5000.0f, 0, m, &cBinop_4MyuHNhK_sendMessage); +} + +void Heavy_delay_simple::cMsg_o1Fk4mWr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "mirror"); + hTable_onMessage(_c, &Context(_c)->hTable_TFqOKFso, 0, m, &hTable_TFqOKFso_sendMessage); +} + +void Heavy_delay_simple::cCast_g87DeymK_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cDelay_onMessage(_c, &Context(_c)->cDelay_OFD5iAqe, 0, m, &cDelay_OFD5iAqe_sendMessage); +} + +void Heavy_delay_simple::cMsg_DlDeZMFB_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setFloat(m, 0, static_cast(HV_N_SIMD)); + cDelay_onMessage(_c, &Context(_c)->cDelay_EI4kKdvf, 2, m, &cDelay_EI4kKdvf_sendMessage); +} + +void Heavy_delay_simple::cTabhead_zhu6BFdy_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_99kJjPmN, HV_BINOP_SUBTRACT, 0, m, &cBinop_99kJjPmN_sendMessage); +} + +void Heavy_delay_simple::cMsg_c68iDIVD_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "samplerate"); + cSystem_onMessage(_c, NULL, 0, m, &cSystem_LlBI3RTT_sendMessage); +} + +void Heavy_delay_simple::cSystem_LlBI3RTT_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_DIVIDE, 1000.0f, 0, m, &cBinop_qp875S9R_sendMessage); +} + +void Heavy_delay_simple::cVar_nONxuBGP_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_2Y6gSDxh_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cDelay_FTWI9Lbr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_FTWI9Lbr, m); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 0, m, &cDelay_4Y58Ksfn_sendMessage); + sTabread_onMessage(_c, &Context(_c)->sTabread_OJVVdLuf, 0, m, &sTabread_OJVVdLuf_sendMessage); +} + +void Heavy_delay_simple::cDelay_4Y58Ksfn_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const m) { + cDelay_clearExecutingMessage(&Context(_c)->cDelay_4Y58Ksfn, m); + sTabread_onMessage(_c, &Context(_c)->sTabread_OJVVdLuf, 0, m, &sTabread_OJVVdLuf_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 0, m, &cDelay_4Y58Ksfn_sendMessage); +} + +void Heavy_delay_simple::sTabread_OJVVdLuf_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + switch (letIn) { + case 0: { + break; + } + case 1: { + cBinop_onMessage(_c, &Context(_c)->cBinop_HsqyfMPx, HV_BINOP_SUBTRACT, 0, m, &cBinop_HsqyfMPx_sendMessage); + break; + } + default: return; + } +} + +void Heavy_delay_simple::cBinop_kd9uMvnd_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_khIQbSXe, HV_BINOP_MAX, 0, m, &cBinop_khIQbSXe_sendMessage); +} + +void Heavy_delay_simple::cBinop_qp875S9R_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_kd9uMvnd, HV_BINOP_MULTIPLY, 0, m, &cBinop_kd9uMvnd_sendMessage); +} + +void Heavy_delay_simple::cBinop_99kJjPmN_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_60hMCcpx_sendMessage(_c, 0, m); + sTabread_onMessage(_c, &Context(_c)->sTabread_OJVVdLuf, 0, m, &sTabread_OJVVdLuf_sendMessage); + cCast_onMessage(_c, HV_CAST_BANG, 0, m, &cCast_zoAHkSwC_sendMessage); +} + +void Heavy_delay_simple::cSystem_kPqFOU4H_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_HsqyfMPx, HV_BINOP_SUBTRACT, 1, m, &cBinop_HsqyfMPx_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 2, m, &cDelay_4Y58Ksfn_sendMessage); +} + +void Heavy_delay_simple::cMsg_2Y6gSDxh_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(3); + msg_init(m, 3, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "table"); + msg_setElementToFrom(m, 1, n, 0); + msg_setSymbol(m, 2, "size"); + cSystem_onMessage(_c, NULL, 0, m, &cSystem_kPqFOU4H_sendMessage); +} + +void Heavy_delay_simple::cMsg_60hMCcpx_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setSymbol(m, 0, "clear"); + cDelay_onMessage(_c, &Context(_c)->cDelay_FTWI9Lbr, 0, m, &cDelay_FTWI9Lbr_sendMessage); + cDelay_onMessage(_c, &Context(_c)->cDelay_4Y58Ksfn, 0, m, &cDelay_4Y58Ksfn_sendMessage); +} + +void Heavy_delay_simple::cMsg_2y7SxePc_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *const n) { + HvMessage *m = nullptr; + m = HV_MESSAGE_ON_STACK(1); + msg_init(m, 1, msg_getTimestamp(n)); + msg_setFloat(m, 0, static_cast(HV_N_SIMD)); + cBinop_onMessage(_c, &Context(_c)->cBinop_khIQbSXe, HV_BINOP_MAX, 1, m, &cBinop_khIQbSXe_sendMessage); +} + +void Heavy_delay_simple::cBinop_khIQbSXe_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_99kJjPmN, HV_BINOP_SUBTRACT, 1, m, &cBinop_99kJjPmN_sendMessage); +} + +void Heavy_delay_simple::cCast_zoAHkSwC_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cDelay_onMessage(_c, &Context(_c)->cDelay_FTWI9Lbr, 0, m, &cDelay_FTWI9Lbr_sendMessage); +} + +void Heavy_delay_simple::cBinop_H69XkGIL_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cDelay_onMessage(_c, &Context(_c)->cDelay_FTWI9Lbr, 2, m, &cDelay_FTWI9Lbr_sendMessage); +} + +void Heavy_delay_simple::cBinop_HsqyfMPx_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_k_onMessage(_c, NULL, HV_BINOP_MULTIPLY, -1.0f, 0, m, &cBinop_H69XkGIL_sendMessage); +} + +void Heavy_delay_simple::cCast_spzeS2Y3_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_nONxuBGP, 0, m, &cVar_nONxuBGP_sendMessage); + cMsg_c68iDIVD_sendMessage(_c, 0, m); + cTabhead_onMessage(_c, &Context(_c)->cTabhead_zhu6BFdy, 0, m, &cTabhead_zhu6BFdy_sendMessage); +} + +void Heavy_delay_simple::cSend_wldZJDJM_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_kE9u8lIG_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cSend_dFhbvIPr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_pzAN0z6v_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cSend_4A35hG1G_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_iG7Q7Uzr_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cSend_jiugLxbD_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cReceive_kJYDDgog_sendMessage(_c, 0, m); +} + +void Heavy_delay_simple::cReceive_kE9u8lIG_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cBinop_onMessage(_c, &Context(_c)->cBinop_kd9uMvnd, HV_BINOP_MULTIPLY, 1, m, &cBinop_kd9uMvnd_sendMessage); + cCast_onMessage(_c, HV_CAST_BANG, 0, m, &cCast_spzeS2Y3_sendMessage); +} + +void Heavy_delay_simple::cReceive_pzAN0z6v_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_o7lB6W7R, m); +} + +void Heavy_delay_simple::cReceive_iG7Q7Uzr_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_4RDRwlaL, m); +} + +void Heavy_delay_simple::cReceive_kJYDDgog_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + sVarf_onMessage(_c, &Context(_c)->sVarf_vL8TEPN9, m); +} + +void Heavy_delay_simple::cReceive_AJBhI0kX_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cMsg_hdrV3jKk_sendMessage(_c, 0, m); + cMsg_2y7SxePc_sendMessage(_c, 0, m); + cVar_onMessage(_c, &Context(_c)->cVar_nONxuBGP, 0, m, &cVar_nONxuBGP_sendMessage); + cMsg_c68iDIVD_sendMessage(_c, 0, m); + cTabhead_onMessage(_c, &Context(_c)->cTabhead_zhu6BFdy, 0, m, &cTabhead_zhu6BFdy_sendMessage); +} + +void Heavy_delay_simple::cReceive_eGza5oBR_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_LnHfGF8V, 0, m, &cVar_LnHfGF8V_sendMessage); +} + +void Heavy_delay_simple::cReceive_SIoX2X2C_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_mOyup5ir, 0, m, &cVar_mOyup5ir_sendMessage); +} + +void Heavy_delay_simple::cReceive_UQL9KHRi_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_4cpXTaBY, 0, m, &cVar_4cpXTaBY_sendMessage); +} + +void Heavy_delay_simple::cReceive_ex2b75Oc_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_fRPZcVCt, 0, m, &cVar_fRPZcVCt_sendMessage); +} + +void Heavy_delay_simple::cReceive_LQwqfjMo_sendMessage(HeavyContextInterface *_c, int letIn, const HvMessage *m) { + cVar_onMessage(_c, &Context(_c)->cVar_Om7ifKfd, 0, m, &cVar_Om7ifKfd_sendMessage); +} + + + + +/* + * Context Process Implementation + */ + +int Heavy_delay_simple::process(float **inputBuffers, float **outputBuffers, int n) { + while (hLp_hasData(&inQueue)) { + hv_uint32_t numBytes = 0; + ReceiverMessagePair *p = reinterpret_cast(hLp_getReadBuffer(&inQueue, &numBytes)); + hv_assert(numBytes >= sizeof(ReceiverMessagePair)); + scheduleMessageForReceiver(p->receiverHash, &p->msg); + hLp_consume(&inQueue); + } + const int n4 = n & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD + + // temporary signal vars + hv_bufferf_t Bf0, Bf1, Bf2, Bf3; + + // input and output vars + hv_bufferf_t O0, O1; + hv_bufferf_t I0, I1; + + // declare and init the zero buffer + hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO)); + + hv_uint32_t nextBlock = blockStartTimestamp; + for (int n = 0; n < n4; n += HV_N_SIMD) { + + // process all of the messages for this block + nextBlock += HV_N_SIMD; + while (mq_hasMessageBefore(&mq, nextBlock)) { + MessageNode *const node = mq_peek(&mq); + node->sendMessage(this, node->let, node->m); + mq_pop(&mq); + } + + // load input buffers + __hv_load_f(inputBuffers[0]+n, VOf(I0)); + __hv_load_f(inputBuffers[1]+n, VOf(I1)); + + // zero output buffers + __hv_zero_f(VOf(O0)); + __hv_zero_f(VOf(O1)); + + // process all signal functions + __hv_add_f(VIf(I0), VIf(I1), VOf(Bf0)); + __hv_varread_f(&sVarf_eLw9NCKn, VOf(Bf1)); + __hv_mul_f(VIf(Bf0), VIf(Bf1), VOf(Bf1)); + __hv_tabread_f(&sTabread_OJVVdLuf, VOf(Bf0)); + __hv_varread_f(&sVarf_o7lB6W7R, VOf(Bf2)); + __hv_varread_f(&sVarf_vL8TEPN9, VOf(Bf3)); + __hv_mul_f(VIf(Bf1), VIf(Bf3), VOf(Bf3)); + __hv_fma_f(VIf(Bf0), VIf(Bf2), VIf(Bf3), VOf(Bf3)); + __hv_varread_f(&sVarf_DXN3F4mN, VOf(Bf2)); + __hv_add_f(VIf(Bf1), VIf(Bf2), VOf(Bf2)); + __hv_tabwrite_f(&sTabwrite_kTfJPrs1, VIf(Bf2)); + __hv_varread_f(&sVarf_4RDRwlaL, VOf(Bf2)); + __hv_mul_f(VIf(Bf0), VIf(Bf2), VOf(Bf2)); + __hv_varwrite_f(&sVarf_DXN3F4mN, VIf(Bf2)); + __hv_add_f(VIf(Bf3), VIf(O0), VOf(O0)); + __hv_add_f(VIf(Bf3), VIf(O1), VOf(O1)); + + // save output vars to output buffer + __hv_store_f(outputBuffers[0]+n, VIf(O0)); + __hv_store_f(outputBuffers[1]+n, VIf(O1)); + } + + blockStartTimestamp = nextBlock; + + return n4; // return the number of frames processed +} + +int Heavy_delay_simple::processInline(float *inputBuffers, float *outputBuffers, int n4) { + hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD + + // define the heavy input buffer for 2 channel(s) + float **const bIn = reinterpret_cast(hv_alloca(2*sizeof(float *))); + bIn[0] = inputBuffers+(0*n4); + bIn[1] = inputBuffers+(1*n4); + + // define the heavy output buffer for 2 channel(s) + float **const bOut = reinterpret_cast(hv_alloca(2*sizeof(float *))); + bOut[0] = outputBuffers+(0*n4); + bOut[1] = outputBuffers+(1*n4); + + int n = process(bIn, bOut, n4); + return n; +} + +int Heavy_delay_simple::processInlineInterleaved(float *inputBuffers, float *outputBuffers, int n4) { + hv_assert(n4 & ~HV_N_SIMD_MASK); // ensure that n4 is a multiple of HV_N_SIMD + + // define the heavy input buffer for 2 channel(s), uninterleave + float *const bIn = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); + #if HV_SIMD_SSE || HV_SIMD_AVX + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + __m128 a = _mm_load_ps(inputBuffers+i); // LRLR + __m128 b = _mm_load_ps(inputBuffers+4+i); // LRLR + __m128 x = _mm_shuffle_ps(a, b, _MM_SHUFFLE(2,0,2,0)); // LLLL + __m128 y = _mm_shuffle_ps(a, b, _MM_SHUFFLE(3,1,3,1)); // RRRR + _mm_store_ps(bIn+j, x); + _mm_store_ps(bIn+n4+j, y); + } + #elif HV_SIMD_NEON + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + float32x4x2_t a = vld2q_f32(inputBuffers+i); // load and uninterleave + vst1q_f32(bIn+j, a.val[0]); + vst1q_f32(bIn+n4+j, a.val[1]); + } + #else // HV_SIMD_NONE + for (int j = 0; j < n4; ++j) { + bIn[0*n4+j] = inputBuffers[0+2*j]; + bIn[1*n4+j] = inputBuffers[1+2*j]; + } + #endif + + // define the heavy output buffer for 2 channel(s) + float *const bOut = reinterpret_cast(hv_alloca(2*n4*sizeof(float))); + + int n = processInline(bIn, bOut, n4); + + // interleave the heavy output into the output buffer + #if HV_SIMD_AVX + for (int i = 0, j = 0; j < n4; j += 8, i += 16) { + __m256 x = _mm256_load_ps(bOut+j); // LLLLLLLL + __m256 y = _mm256_load_ps(bOut+n4+j); // RRRRRRRR + __m256 a = _mm256_unpacklo_ps(x, y); // LRLRLRLR + __m256 b = _mm256_unpackhi_ps(x, y); // LRLRLRLR + _mm256_store_ps(outputBuffers+i, a); + _mm256_store_ps(outputBuffers+8+i, b); + } + #elif HV_SIMD_SSE + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + __m128 x = _mm_load_ps(bOut+j); // LLLL + __m128 y = _mm_load_ps(bOut+n4+j); // RRRR + __m128 a = _mm_unpacklo_ps(x, y); // LRLR + __m128 b = _mm_unpackhi_ps(x, y); // LRLR + _mm_store_ps(outputBuffers+i, a); + _mm_store_ps(outputBuffers+4+i, b); + } + #elif HV_SIMD_NEON + // https://community.arm.com/groups/processors/blog/2012/03/13/coding-for-neon--part-5-rearranging-vectors + for (int i = 0, j = 0; j < n4; j += 4, i += 8) { + float32x4_t x = vld1q_f32(bOut+j); + float32x4_t y = vld1q_f32(bOut+n4+j); + float32x4x2_t z = {x, y}; + vst2q_f32(outputBuffers+i, z); // interleave and store + } + #else // HV_SIMD_NONE + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < n4; ++j) { + outputBuffers[i+2*j] = bOut[i*n4+j]; + } + } + #endif + + return n; +} diff --git a/delay_simple/plugin/source/Heavy_delay_simple.h b/delay_simple/plugin/source/Heavy_delay_simple.h new file mode 100644 index 0000000..6c68048 --- /dev/null +++ b/delay_simple/plugin/source/Heavy_delay_simple.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HEAVY_DELAY_SIMPLE_H_ +#define _HEAVY_DELAY_SIMPLE_H_ + +#include "HvHeavy.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if HV_APPLE +#pragma mark - Heavy Context +#endif + +typedef enum { + HV_DELAY_SIMPLE_PARAM_IN_DELAY_FEEDBACK = 0x36377AA9, // Delay_Feedback + HV_DELAY_SIMPLE_PARAM_IN_DELAY_TIME = 0xEE78D101, // Delay_Time + HV_DELAY_SIMPLE_PARAM_IN_DRY_VOLUME = 0x5EFB46D2, // Dry_Volume + HV_DELAY_SIMPLE_PARAM_IN_GAIN = 0x677821DA, // Gain + HV_DELAY_SIMPLE_PARAM_IN_WET_VOLUME = 0xEA49B5A, // Wet_Volume +} Hv_delay_simple_ParameterIn; + + +/** + * Creates a new patch instance. + * Sample rate should be positive and in Hertz, e.g. 44100.0. + */ +HeavyContextInterface *hv_delay_simple_new(double sampleRate); + +/** + * Creates a new patch instance. + * @param sampleRate Sample rate should be positive (> 0) and in Hertz, e.g. 48000.0. + * @param poolKb Pool size is in kilobytes, and determines the maximum amount of memory + * allocated to messages at any time. By default this is 10 KB. + * @param inQueueKb The size of the input message queue in kilobytes. It determines the + * amount of memory dedicated to holding scheduled messages between calls to + * process(). Default is 2 KB. + * @param outQueueKb The size of the output message queue in kilobytes. It determines the + * amount of memory dedicated to holding scheduled messages to the default sendHook. + * See getNextSentMessage() for info on accessing these messages. Default is 0 KB. + */ +HeavyContextInterface *hv_delay_simple_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb); + +/** + * Free the patch instance. + */ +void hv_delay_simple_free(HeavyContextInterface *instance); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_DELAY_SIMPLE_H_ diff --git a/delay_simple/plugin/source/Heavy_delay_simple.hpp b/delay_simple/plugin/source/Heavy_delay_simple.hpp new file mode 100644 index 0000000..8e1f353 --- /dev/null +++ b/delay_simple/plugin/source/Heavy_delay_simple.hpp @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2022 Enzien Audio, Ltd. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the phrase "powered by heavy", + * the heavy logo, and a hyperlink to https://enzienaudio.com, all in a visible + * form. + * + * 2.1 If the Application is distributed in a store system (for example, + * the Apple "App Store" or "Google Play"), the phrase "powered by heavy" + * shall be included in the app description or the copyright text as well as + * the in the app itself. The heavy logo will shall be visible in the app + * itself as well. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HEAVY_CONTEXT_DELAY_SIMPLE_HPP_ +#define _HEAVY_CONTEXT_DELAY_SIMPLE_HPP_ + +// object includes +#include "HeavyContext.hpp" +#include "HvControlTabhead.h" +#include "HvSignalVar.h" +#include "HvTable.h" +#include "HvControlBinop.h" +#include "HvSignalTabwrite.h" +#include "HvControlDelay.h" +#include "HvMath.h" +#include "HvSignalTabread.h" +#include "HvControlSystem.h" +#include "HvControlVar.h" +#include "HvControlCast.h" + +class Heavy_delay_simple : public HeavyContext { + + public: + Heavy_delay_simple(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0); + ~Heavy_delay_simple(); + + const char *getName() override { return "delay_simple"; } + int getNumInputChannels() override { return 2; } + int getNumOutputChannels() override { return 2; } + + int process(float **inputBuffers, float **outputBuffer, int n) override; + int processInline(float *inputBuffers, float *outputBuffer, int n) override; + int processInlineInterleaved(float *inputBuffers, float *outputBuffer, int n) override; + + int getParameterInfo(int index, HvParameterInfo *info) override; + struct Parameter { + struct In { + enum ParameterIn : hv_uint32_t { + DELAY_FEEDBACK = 0x36377AA9, // Delay_Feedback + DELAY_TIME = 0xEE78D101, // Delay_Time + DRY_VOLUME = 0x5EFB46D2, // Dry_Volume + GAIN = 0x677821DA, // Gain + WET_VOLUME = 0xEA49B5A, // Wet_Volume + }; + }; + }; + + private: + HvTable *getTableForHash(hv_uint32_t tableHash) override; + void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override; + + // static sendMessage functions + static void cVar_4cpXTaBY_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_fRPZcVCt_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_Om7ifKfd_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_LnHfGF8V_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_mOyup5ir_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_hdrV3jKk_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSystem_B288dglo_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_OFD5iAqe_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_EI4kKdvf_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_4MyuHNhK_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void hTable_TFqOKFso_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_clwxczYs_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_NAf7xjjy_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_o1Fk4mWr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cCast_g87DeymK_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_DlDeZMFB_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cTabhead_zhu6BFdy_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_c68iDIVD_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSystem_LlBI3RTT_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cVar_nONxuBGP_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_FTWI9Lbr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cDelay_4Y58Ksfn_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void sTabread_OJVVdLuf_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_kd9uMvnd_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_qp875S9R_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_99kJjPmN_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSystem_kPqFOU4H_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_2Y6gSDxh_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_60hMCcpx_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cMsg_2y7SxePc_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_khIQbSXe_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cCast_zoAHkSwC_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_H69XkGIL_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cBinop_HsqyfMPx_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cCast_spzeS2Y3_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_wldZJDJM_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_dFhbvIPr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_4A35hG1G_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cSend_jiugLxbD_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_kE9u8lIG_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_pzAN0z6v_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_iG7Q7Uzr_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_kJYDDgog_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_AJBhI0kX_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_eGza5oBR_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_SIoX2X2C_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_UQL9KHRi_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_ex2b75Oc_sendMessage(HeavyContextInterface *, int, const HvMessage *); + static void cReceive_LQwqfjMo_sendMessage(HeavyContextInterface *, int, const HvMessage *); + + // objects + SignalTabread sTabread_OJVVdLuf; + SignalTabwrite sTabwrite_kTfJPrs1; + ControlVar cVar_4cpXTaBY; + ControlVar cVar_fRPZcVCt; + ControlVar cVar_Om7ifKfd; + ControlVar cVar_LnHfGF8V; + ControlVar cVar_mOyup5ir; + ControlDelay cDelay_OFD5iAqe; + ControlDelay cDelay_EI4kKdvf; + ControlBinop cBinop_4MyuHNhK; + HvTable hTable_TFqOKFso; + ControlBinop cBinop_NAf7xjjy; + ControlTabhead cTabhead_zhu6BFdy; + ControlVar cVar_nONxuBGP; + ControlDelay cDelay_FTWI9Lbr; + ControlDelay cDelay_4Y58Ksfn; + ControlBinop cBinop_kd9uMvnd; + ControlBinop cBinop_qp875S9R; + ControlBinop cBinop_99kJjPmN; + ControlBinop cBinop_khIQbSXe; + ControlBinop cBinop_H69XkGIL; + ControlBinop cBinop_HsqyfMPx; + SignalVarf sVarf_DXN3F4mN; + SignalVarf sVarf_o7lB6W7R; + SignalVarf sVarf_4RDRwlaL; + SignalVarf sVarf_vL8TEPN9; + SignalVarf sVarf_eLw9NCKn; +}; + +#endif // _HEAVY_CONTEXT_DELAY_SIMPLE_HPP_ diff --git a/delay_simple/plugin/source/HvControlBinop.c b/delay_simple/plugin/source/HvControlBinop.c new file mode 100644 index 0000000..cc248d9 --- /dev/null +++ b/delay_simple/plugin/source/HvControlBinop.c @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlBinop.h" + +hv_size_t cBinop_init(ControlBinop *o, float k) { + o->k = k; + return 0; +} + +static float cBinop_perform_op(BinopType op, float f, float k) { + switch (op) { + case HV_BINOP_ADD: return f + k; + case HV_BINOP_SUBTRACT: return f - k; + case HV_BINOP_MULTIPLY: return f * k; + case HV_BINOP_DIVIDE: return (k != 0.0f) ? (f/k) : 0.0f; + case HV_BINOP_INT_DIV: { + const int ik = (int) k; + return (ik != 0) ? (float) (((int) f) / ik) : 0.0f; + } + case HV_BINOP_MOD_BIPOLAR: { + const int ik = (int) k; + return (ik != 0) ? (float) (((int) f) % ik) : 0.0f; + } + case HV_BINOP_MOD_UNIPOLAR: { + f = (k == 0.0f) ? 0.0f : (float) ((int) f % (int) k); + return (f < 0.0f) ? f + hv_abs_f(k) : f; + } + case HV_BINOP_BIT_LEFTSHIFT: return (float) (((int) f) << ((int) k)); + case HV_BINOP_BIT_RIGHTSHIFT: return (float) (((int) f) >> ((int) k)); + case HV_BINOP_BIT_AND: return (float) ((int) f & (int) k); + case HV_BINOP_BIT_XOR: return (float) ((int) f ^ (int) k); + case HV_BINOP_BIT_OR: return (float) ((int) f | (int) k); + case HV_BINOP_EQ: return (f == k) ? 1.0f : 0.0f; + case HV_BINOP_NEQ: return (f != k) ? 1.0f : 0.0f; + case HV_BINOP_LOGICAL_AND: return ((f == 0.0f) || (k == 0.0f)) ? 0.0f : 1.0f; + case HV_BINOP_LOGICAL_OR: return ((f == 0.0f) && (k == 0.0f)) ? 0.0f : 1.0f; + case HV_BINOP_LESS_THAN: return (f < k) ? 1.0f : 0.0f; + case HV_BINOP_LESS_THAN_EQL: return (f <= k) ? 1.0f : 0.0f; + case HV_BINOP_GREATER_THAN: return (f > k) ? 1.0f : 0.0f; + case HV_BINOP_GREATER_THAN_EQL: return (f >= k) ? 1.0f : 0.0f; + case HV_BINOP_MAX: return hv_max_f(f, k); + case HV_BINOP_MIN: return hv_min_f(f, k); + case HV_BINOP_POW: return (f > 0.0f) ? hv_pow_f(f, k) : 0.0f; + case HV_BINOP_ATAN2: return ((f == 0.0f) && (k == 0.0f)) ? 0.0f : hv_atan2_f(f, k); + default: return 0.0f; + } +} + +void cBinop_onMessage(HeavyContextInterface *_c, ControlBinop *o, BinopType op, int letIn, + const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (msg_isFloat(m, 0)) { + // Note(joe): supporting Pd's ability to perform operations of packs + // of floats is likely to not be supported in the future. + if (msg_isFloat(m, 1)) o->k = msg_getFloat(m, 1); + HvMessage *n = HV_MESSAGE_ON_STACK(1); + float f = cBinop_perform_op(op, msg_getFloat(m, 0), o->k); + msg_initWithFloat(n, msg_getTimestamp(m), f); + sendMessage(_c, 0, n); + } + break; + } + case 1: { + if (msg_isFloat(m, 0)) { + o->k = msg_getFloat(m, 0); + } + break; + } + default: break; + } +} + +void cBinop_k_onMessage(HeavyContextInterface *_c, void *o, BinopType op, float k, + int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (msg_isFloat(m, 0)) { + // NOTE(mhroth): Heavy does not support sending bangs to binop objects to return the previous output + float f = (msg_isFloat(m, 1)) ? msg_getFloat(m, 1) : k; + HvMessage *n = HV_MESSAGE_ON_STACK(1); + f = cBinop_perform_op(op, msg_getFloat(m, 0), f); + msg_initWithFloat(n, msg_getTimestamp(m), f); + sendMessage(_c, 0, n); + } +} diff --git a/delay_simple/plugin/source/HvControlBinop.h b/delay_simple/plugin/source/HvControlBinop.h new file mode 100644 index 0000000..e6d197a --- /dev/null +++ b/delay_simple/plugin/source/HvControlBinop.h @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_BINOP_H_ +#define _HEAVY_CONTROL_BINOP_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum BinopType { + HV_BINOP_ADD, + HV_BINOP_SUBTRACT, + HV_BINOP_MULTIPLY, + HV_BINOP_DIVIDE, + HV_BINOP_INT_DIV, + HV_BINOP_MOD_BIPOLAR, + HV_BINOP_MOD_UNIPOLAR, + HV_BINOP_BIT_LEFTSHIFT, + HV_BINOP_BIT_RIGHTSHIFT, + HV_BINOP_BIT_AND, + HV_BINOP_BIT_XOR, + HV_BINOP_BIT_OR, + HV_BINOP_EQ, + HV_BINOP_NEQ, + HV_BINOP_LOGICAL_AND, + HV_BINOP_LOGICAL_OR, + HV_BINOP_LESS_THAN, + HV_BINOP_LESS_THAN_EQL, + HV_BINOP_GREATER_THAN, + HV_BINOP_GREATER_THAN_EQL, + HV_BINOP_MAX, + HV_BINOP_MIN, + HV_BINOP_POW, + HV_BINOP_ATAN2 +} BinopType; + +typedef struct ControlBinop { + float k; +} ControlBinop; + +hv_size_t cBinop_init(ControlBinop *o, float k); + +void cBinop_onMessage(HeavyContextInterface *_c, ControlBinop *o, BinopType op, int letIn, + const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +void cBinop_k_onMessage(HeavyContextInterface *_c, void *o, BinopType op, float k, + int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_BINOP_H_ diff --git a/delay_simple/plugin/source/HvControlCast.c b/delay_simple/plugin/source/HvControlCast.c new file mode 100644 index 0000000..8bf3892 --- /dev/null +++ b/delay_simple/plugin/source/HvControlCast.c @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlCast.h" + +void cCast_onMessage(HeavyContextInterface *_c, CastType castType, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (castType) { + case HV_CAST_BANG: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithBang(n, msg_getTimestamp(m)); + sendMessage(_c, 0, n); + break; + } + case HV_CAST_FLOAT: { + if (msg_isFloat(m, 0)) { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), msg_getFloat(m, 0)); + sendMessage(_c, 0, n); + } + break; + } + case HV_CAST_SYMBOL: { + switch (msg_getType(m, 0)) { + case HV_MSG_BANG: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithSymbol(n, msg_getTimestamp(m), "bang"); + sendMessage(_c, 0, n); + break; + } + case HV_MSG_FLOAT: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithSymbol(n, msg_getTimestamp(m), "float"); + sendMessage(_c, 0, n); + break; + } + case HV_MSG_SYMBOL: { + sendMessage(_c, 0, m); + break; + } + default: return; + } + break; + } + default: return; + } +} diff --git a/delay_simple/plugin/source/HvControlCast.h b/delay_simple/plugin/source/HvControlCast.h new file mode 100644 index 0000000..1952f6d --- /dev/null +++ b/delay_simple/plugin/source/HvControlCast.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_CAST_H_ +#define _HEAVY_CONTROL_CAST_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum CastType { + HV_CAST_BANG, + HV_CAST_FLOAT, + HV_CAST_SYMBOL +} CastType; + +void cCast_onMessage(HeavyContextInterface *_c, CastType castType, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_CAST_H_ diff --git a/delay_simple/plugin/source/HvControlDelay.c b/delay_simple/plugin/source/HvControlDelay.c new file mode 100644 index 0000000..fd8aff9 --- /dev/null +++ b/delay_simple/plugin/source/HvControlDelay.c @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlDelay.h" + +hv_size_t cDelay_init(HeavyContextInterface *_c, ControlDelay *o, float delayMs) { + o->delay = hv_millisecondsToSamples(_c, delayMs); + hv_memclear(o->msgs, __HV_DELAY_MAX_MESSAGES*sizeof(HvMessage *)); + return 0; +} + +void cDelay_onMessage(HeavyContextInterface *_c, ControlDelay *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (msg_compareSymbol(m, 0, "flush")) { + // send all messages immediately + for (int i = 0; i < __HV_DELAY_MAX_MESSAGES; i++) { + HvMessage *n = o->msgs[i]; + if (n != NULL) { + msg_setTimestamp(n, msg_getTimestamp(m)); // update the timestamp to now + sendMessage(_c, 0, n); // send the message + hv_cancelMessage(_c, n, sendMessage); // then clear it + // NOTE(mhroth): there may be a problem here if a flushed message causes a clear message to return + // to this object in the same step + } + } + hv_memclear(o->msgs, __HV_DELAY_MAX_MESSAGES*sizeof(HvMessage *)); + } else if (msg_compareSymbol(m, 0, "clear")) { + // cancel (clear) all (pending) messages + for (int i = 0; i < __HV_DELAY_MAX_MESSAGES; i++) { + HvMessage *n = o->msgs[i]; + if (n != NULL) { + hv_cancelMessage(_c, n, sendMessage); + } + } + hv_memclear(o->msgs, __HV_DELAY_MAX_MESSAGES*sizeof(HvMessage *)); + } else { + hv_uint32_t ts = msg_getTimestamp(m); + msg_setTimestamp((HvMessage *) m, ts+o->delay); // update the timestamp to set the delay + int i; + for (i = 0; i < __HV_DELAY_MAX_MESSAGES; i++) { + if (o->msgs[i] == NULL) { + o->msgs[i] = hv_scheduleMessageForObject(_c, m, sendMessage, 0); + break; + } + } + hv_assert((i < __HV_DELAY_MAX_MESSAGES) && // scheduled message limit reached + "[__delay] cannot track any more messages. Try increasing the size of __HV_DELAY_MAX_MESSAGES."); + msg_setTimestamp((HvMessage *) m, ts); // return to the original timestamp + } + break; + } + case 1: { + if (msg_isFloat(m,0)) { + // set delay in milliseconds (cannot be negative!) + o->delay = hv_millisecondsToSamples(_c, msg_getFloat(m,0)); + } + break; + } + case 2: { + if (msg_isFloat(m,0)) { + // set delay in samples (cannot be negative!) + o->delay = (hv_uint32_t) hv_max_f(0.0f, msg_getFloat(m,0)); + } + break; + } + default: break; + } +} + +void cDelay_clearExecutingMessage(ControlDelay *o, const HvMessage *m) { + for (int i = 0; i < __HV_DELAY_MAX_MESSAGES; ++i) { + if (o->msgs[i] == m) { + o->msgs[i] = NULL; + break; + } + } +} diff --git a/delay_simple/plugin/source/HvControlDelay.h b/delay_simple/plugin/source/HvControlDelay.h new file mode 100644 index 0000000..b2943fb --- /dev/null +++ b/delay_simple/plugin/source/HvControlDelay.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_DELAY_H_ +#define _HEAVY_CONTROL_DELAY_H_ + +#define __HV_DELAY_MAX_MESSAGES 8 + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ControlDelay { + hv_uint32_t delay; // delay in samples + HvMessage *msgs[__HV_DELAY_MAX_MESSAGES]; +} ControlDelay; + +hv_size_t cDelay_init(HeavyContextInterface *_c, ControlDelay *o, float delayMs); + +void cDelay_onMessage(HeavyContextInterface *_c, ControlDelay *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +void cDelay_clearExecutingMessage(ControlDelay *o, const HvMessage *m); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_DELAY_H_ diff --git a/delay_simple/plugin/source/HvControlSystem.c b/delay_simple/plugin/source/HvControlSystem.c new file mode 100644 index 0000000..a1ad30f --- /dev/null +++ b/delay_simple/plugin/source/HvControlSystem.c @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlSystem.h" + +void cSystem_onMessage(HeavyContextInterface *_c, void *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + + HvMessage *n = HV_MESSAGE_ON_STACK(1); + if (msg_compareSymbol(m, 0, "samplerate")) { + + msg_initWithFloat(n, msg_getTimestamp(m), (float) hv_getSampleRate(_c)); + } else if (msg_compareSymbol(m, 0, "numInputChannels")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hv_getNumInputChannels(_c)); + } else if (msg_compareSymbol(m, 0, "numOutputChannels")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hv_getNumOutputChannels(_c)); + } else if (msg_compareSymbol(m, 0, "currentTime")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) msg_getTimestamp(m)); + } else if (msg_compareSymbol(m, 0, "table")) { + // NOTE(mhroth): no need to check message format for symbols as table lookup will fail otherwise + HvTable *table = hv_table_get(_c, msg_getHash(m,1)); + if (table != NULL) { + if (msg_compareSymbol(m, 2, "length")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getLength(table)); + } else if (msg_compareSymbol(m, 2, "size")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(table)); + } else if (msg_compareSymbol(m, 2, "head")) { + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getHead(table)); + } else return; + } else return; + } else return; + sendMessage(_c, 0, n); +} diff --git a/delay_simple/plugin/source/HvControlSystem.h b/delay_simple/plugin/source/HvControlSystem.h new file mode 100644 index 0000000..42da7bd --- /dev/null +++ b/delay_simple/plugin/source/HvControlSystem.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_SYSTEM_H_ +#define _HEAVY_CONTROL_SYSTEM_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void cSystem_onMessage(HeavyContextInterface *_c, void *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_SYSTEM_H_ diff --git a/delay_simple/plugin/source/HvControlTabhead.c b/delay_simple/plugin/source/HvControlTabhead.c new file mode 100644 index 0000000..586ed78 --- /dev/null +++ b/delay_simple/plugin/source/HvControlTabhead.c @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlTabhead.h" + +hv_size_t cTabhead_init(ControlTabhead *o, HvTable *table) { + o->table = table; + return 0; +} + +void cTabhead_onMessage(HeavyContextInterface *_c, ControlTabhead *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (msg_getType(m,0) == HV_MSG_BANG) { + // get current head of table + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getHead(o->table)); + sendMessage(_c, 0, n); + } + break; + } + case 1: { + if (msg_isHashLike(m,0)) { + // set a new table + o->table = hv_table_get(_c, msg_getHash(m,0)); + } + break; + } + default: break; + } +} diff --git a/delay_simple/plugin/source/HvControlTabhead.h b/delay_simple/plugin/source/HvControlTabhead.h new file mode 100644 index 0000000..c65dadd --- /dev/null +++ b/delay_simple/plugin/source/HvControlTabhead.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_TABHEAD_H_ +#define _HEAVY_CONTROL_TABHEAD_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ControlTabhead { + struct HvTable *table; +} ControlTabhead; + +hv_size_t cTabhead_init(ControlTabhead *o, struct HvTable *table); + +void cTabhead_onMessage(HeavyContextInterface *_c, ControlTabhead *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_TABHEAD_H_ diff --git a/delay_simple/plugin/source/HvControlVar.c b/delay_simple/plugin/source/HvControlVar.c new file mode 100644 index 0000000..80f22b9 --- /dev/null +++ b/delay_simple/plugin/source/HvControlVar.c @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvControlVar.h" + +hv_size_t cVar_init_f(ControlVar *o, float k) { + o->e.type = HV_MSG_FLOAT; + o->e.data.f = k; + return 0; +} + +hv_size_t cVar_init_s(ControlVar *o, const char *s) { + o->e.type = HV_MSG_HASH; + o->e.data.h = hv_string_to_hash(s); + return 0; +} + +void cVar_free(ControlVar *o) { + // nothing to do +} + +void cVar_onMessage(HeavyContextInterface *_c, ControlVar *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + switch (msg_getType(m,0)) { + case HV_MSG_BANG: { + HvMessage *n = HV_MESSAGE_ON_STACK(1); + if (o->e.type == HV_MSG_FLOAT) msg_initWithFloat(n, msg_getTimestamp(m), o->e.data.f); + else if (o->e.type == HV_MSG_HASH) msg_initWithHash(n, msg_getTimestamp(m), o->e.data.h); + else return; + sendMessage(_c, 0, n); + break; + } + case HV_MSG_FLOAT: { + o->e.type = HV_MSG_FLOAT; + o->e.data.f = msg_getFloat(m,0); + sendMessage(_c, 0, m); + break; + } + case HV_MSG_SYMBOL: + case HV_MSG_HASH: { + o->e.type = HV_MSG_HASH; + o->e.data.h = msg_getHash(m,0); + sendMessage(_c, 0, m); + break; + } + default: return; + } + break; + } + case 1: { + switch (msg_getType(m,0)) { + case HV_MSG_FLOAT: { + o->e.type = HV_MSG_FLOAT; + o->e.data.f = msg_getFloat(m,0); + break; + } + case HV_MSG_SYMBOL: + case HV_MSG_HASH: { + o->e.type = HV_MSG_HASH; + o->e.data.h = msg_getHash(m,0); + break; + } + default: break; + } + } + default: return; + } +} diff --git a/delay_simple/plugin/source/HvControlVar.h b/delay_simple/plugin/source/HvControlVar.h new file mode 100644 index 0000000..bf39ade --- /dev/null +++ b/delay_simple/plugin/source/HvControlVar.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_CONTROL_VAR_H_ +#define _HEAVY_CONTROL_VAR_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ControlVar { + Element e; // type is only every HV_MSG_FLOAT or HV_MSG_HASH +} ControlVar; + +hv_size_t cVar_init_f(ControlVar *o, float k); + +hv_size_t cVar_init_s(ControlVar *o, const char *s); + +void cVar_free(ControlVar *o); + +void cVar_onMessage(HeavyContextInterface *_c, ControlVar *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_CONTROL_VAR_H_ diff --git a/delay_simple/plugin/source/HvHeavy.cpp b/delay_simple/plugin/source/HvHeavy.cpp new file mode 100644 index 0000000..9f70f6c --- /dev/null +++ b/delay_simple/plugin/source/HvHeavy.cpp @@ -0,0 +1,318 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HeavyContext.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !HV_WIN +#pragma mark - Heavy Table +#endif + +HV_EXPORT bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength) { + hv_assert(c != nullptr); + return c->setLengthForTable(tableHash, newSampleLength); +} + +HV_EXPORT float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash) { + hv_assert(c != nullptr); + return c->getBufferForTable(tableHash); +} + +HV_EXPORT hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash) { + hv_assert(c != nullptr); + return c->getLengthForTable(tableHash); +} + + + +#if !HV_WIN +#pragma mark - Heavy Message +#endif + +HV_EXPORT hv_size_t hv_msg_getByteSize(hv_uint32_t numElements) { + return msg_getCoreSize(numElements); +} + +HV_EXPORT void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp) { + msg_init(m, numElements, timestamp); +} + +HV_EXPORT hv_size_t hv_msg_getNumElements(const HvMessage *m) { + return msg_getNumElements(m); +} + +HV_EXPORT hv_uint32_t hv_msg_getTimestamp(const HvMessage *m) { + return msg_getTimestamp(m); +} + +HV_EXPORT void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { + msg_setTimestamp(m, timestamp); +} + +HV_EXPORT bool hv_msg_isBang(const HvMessage *const m, int i) { + return msg_isBang(m,i); +} + +HV_EXPORT void hv_msg_setBang(HvMessage *m, int i) { + msg_setBang(m,i); +} + +HV_EXPORT bool hv_msg_isFloat(const HvMessage *const m, int i) { + return msg_isFloat(m, i); +} + +HV_EXPORT float hv_msg_getFloat(const HvMessage *const m, int i) { + return msg_getFloat(m,i); +} + +HV_EXPORT void hv_msg_setFloat(HvMessage *m, int i, float f) { + msg_setFloat(m,i,f); +} + +HV_EXPORT bool hv_msg_isSymbol(const HvMessage *const m, int i) { + return msg_isSymbol(m,i); +} + +HV_EXPORT const char *hv_msg_getSymbol(const HvMessage *const m, int i) { + return msg_getSymbol(m,i); +} + +HV_EXPORT void hv_msg_setSymbol(HvMessage *m, int i, const char *s) { + msg_setSymbol(m,i,s); +} + +HV_EXPORT bool hv_msg_isHash(const HvMessage *const m, int i) { + return msg_isHash(m, i); +} + +HV_EXPORT hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i) { + return msg_getHash(m, i); +} + +HV_EXPORT bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt) { + return msg_hasFormat(m, fmt); +} + +HV_EXPORT char *hv_msg_toString(const HvMessage *const m) { + return msg_toString(m); +} + +HV_EXPORT HvMessage *hv_msg_copy(const HvMessage *const m) { + return msg_copy(m); +} + +HV_EXPORT void hv_msg_free(HvMessage *m) { + msg_free(m); +} + + + +#if !HV_WIN +#pragma mark - Heavy Common +#endif + +HV_EXPORT int hv_getSize(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return (int) c->getSize(); +} + +HV_EXPORT double hv_getSampleRate(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getSampleRate(); +} + +HV_EXPORT int hv_getNumInputChannels(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getNumInputChannels(); +} + +HV_EXPORT int hv_getNumOutputChannels(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getNumOutputChannels(); +} + +HV_EXPORT void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f) { + hv_assert(c != nullptr); + c->setPrintHook(f); +} + +HV_EXPORT HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getPrintHook(); +} + +HV_EXPORT void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f) { + hv_assert(c != nullptr); + c->setSendHook(f); +} + +HV_EXPORT hv_uint32_t hv_stringToHash(const char *s) { + return hv_string_to_hash(s); +} + +HV_EXPORT bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash) { + hv_assert(c != nullptr); + return c->sendBangToReceiver(receiverHash); +} + +HV_EXPORT bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, float x) { + hv_assert(c != nullptr); + return c->sendFloatToReceiver(receiverHash, x); +} + +HV_EXPORT bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s) { + hv_assert(c != nullptr); + return c->sendSymbolToReceiver(receiverHash, s); +} + +HV_EXPORT bool hv_sendMessageToReceiverV( + HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...) { + hv_assert(c != nullptr); + hv_assert(delayMs >= 0.0); + hv_assert(format != nullptr); + + va_list ap; + va_start(ap, format); + const int numElem = (int) hv_strlen(format); + HvMessage *m = HV_MESSAGE_ON_STACK(numElem); + msg_init(m, numElem, c->getCurrentSample() + (hv_uint32_t) (hv_max_d(0.0, delayMs)*c->getSampleRate()/1000.0)); + for (int i = 0; i < numElem; i++) { + switch (format[i]) { + case 'b': msg_setBang(m, i); break; + case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break; + case 'h': msg_setHash(m, i, (int) va_arg(ap, int)); break; + case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break; + default: break; + } + } + va_end(ap); + + return c->sendMessageToReceiver(receiverHash, delayMs, m); +} + +HV_EXPORT bool hv_sendMessageToReceiver( + HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m) { + hv_assert(c != nullptr); + return c->sendMessageToReceiver(receiverHash, delayMs, m); +} + +HV_EXPORT void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + hv_assert(c != nullptr); + c->cancelMessage(m, sendMessage); +} + +HV_EXPORT const char *hv_getName(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getName(); +} + +HV_EXPORT void hv_setUserData(HeavyContextInterface *c, void *userData) { + hv_assert(c != nullptr); + c->setUserData(userData); +} + +HV_EXPORT void *hv_getUserData(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getUserData(); +} + +HV_EXPORT double hv_getCurrentTime(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return (double) c->samplesToMilliseconds(c->getCurrentSample()); +} + +HV_EXPORT hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->getCurrentSample(); +} + +HV_EXPORT float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples) { + hv_assert(c != nullptr); + return c->samplesToMilliseconds(numSamples); +} + +HV_EXPORT hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms) { + hv_assert(c != nullptr); + return c->millisecondsToSamples(ms); +} + +HV_EXPORT int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info) { + hv_assert(c != nullptr); + return c->getParameterInfo(index, info); +} + +HV_EXPORT void hv_lock_acquire(HeavyContextInterface *c) { + hv_assert(c != nullptr); + c->lockAcquire(); +} + +HV_EXPORT bool hv_lock_try(HeavyContextInterface *c) { + hv_assert(c != nullptr); + return c->lockTry(); +} + +HV_EXPORT void hv_lock_release(HeavyContextInterface *c) { + hv_assert(c != nullptr); + c->lockRelease(); +} + +HV_EXPORT void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb) { + hv_assert(c != nullptr); + c->setInputMessageQueueSize(inQueueKb); +} + +HV_EXPORT void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb) { + hv_assert(c != nullptr); + c->setOutputMessageQueueSize(outQueueKb); +} + +HV_EXPORT bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength) { + hv_assert(c != nullptr); + hv_assert(destinationHash != nullptr); + hv_assert(outMsg != nullptr); + return c->getNextSentMessage(destinationHash, outMsg, msgLength); +} + + +#if !HV_WIN +#pragma mark - Heavy Common +#endif + +HV_EXPORT int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n) { + hv_assert(c != nullptr); + return c->process(inputBuffers, outputBuffers, n); +} + +HV_EXPORT int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { + hv_assert(c != nullptr); + return c->processInline(inputBuffers, outputBuffers, n); +} + +HV_EXPORT int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n) { + hv_assert(c != nullptr); + return c->processInlineInterleaved(inputBuffers, outputBuffers, n); +} + +HV_EXPORT void hv_delete(HeavyContextInterface *c) { + delete c; +} + +#ifdef __cplusplus +} +#endif diff --git a/delay_simple/plugin/source/HvHeavy.h b/delay_simple/plugin/source/HvHeavy.h new file mode 100644 index 0000000..cb1aecf --- /dev/null +++ b/delay_simple/plugin/source/HvHeavy.h @@ -0,0 +1,413 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_H_ +#define _HEAVY_H_ + +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _HEAVY_DECLARATIONS_ +#define _HEAVY_DECLARATIONS_ + +#ifdef __cplusplus +class HeavyContextInterface; +#else +typedef struct HeavyContextInterface HeavyContextInterface; +#endif + +typedef struct HvMessage HvMessage; + +typedef enum { + HV_PARAM_TYPE_PARAMETER_IN, + HV_PARAM_TYPE_PARAMETER_OUT, + HV_PARAM_TYPE_EVENT_IN, + HV_PARAM_TYPE_EVENT_OUT +} HvParameterType; + +typedef struct HvParameterInfo { + const char *name; // the human readable parameter name + hv_uint32_t hash; // an integer identified used by heavy for this parameter + HvParameterType type; // type of this parameter + float minVal; // the minimum value of this parameter + float maxVal; // the maximum value of this parameter + float defaultVal; // the default value of this parameter +} HvParameterInfo; + +typedef void (HvSendHook_t) (HeavyContextInterface *context, const char *sendName, hv_uint32_t sendHash, const HvMessage *msg); +typedef void (HvPrintHook_t) (HeavyContextInterface *context, const char *printName, const char *str, const HvMessage *msg); + +#endif // _HEAVY_DECLARATIONS_ + + + +#if HV_APPLE +#pragma mark - Heavy Context +#endif + +/** Deletes a patch instance. */ +void hv_delete(HeavyContextInterface *c); + + + +#if HV_APPLE +#pragma mark - Heavy Process +#endif + +/** + * Processes one block of samples for a patch instance. The buffer format is an array of float channel arrays. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [[LLLL][RRRR]] + * This function support in-place processing. + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ +int hv_process(HeavyContextInterface *c, float **inputBuffers, float **outputBuffers, int n); + +/** + * Processes one block of samples for a patch instance. The buffer format is an uninterleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LLLLRRRR] + * This function support in-place processing. + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ +int hv_processInline(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); + +/** + * Processes one block of samples for a patch instance. The buffer format is an interleaved float array of channels. + * If the context has not input or output channels, the respective argument may be NULL. + * The number of samples to to tbe processed should be a multiple of 1, 4, or 8, depending on if + * no, SSE or NEON, or AVX optimisation is being used, respectively. + * e.g. [LRLRLRLR] + * This function support in-place processing. + * + * @return The number of samples processed. + * + * This function is NOT thread-safe. It is assumed that only the audio thread will execute this function. + */ +int hv_processInlineInterleaved(HeavyContextInterface *c, float *inputBuffers, float *outputBuffers, int n); + + + +#if HV_APPLE +#pragma mark - Heavy Common +#endif + +/** + * Returns the total size in bytes of the context. + * This value may change if tables are resized. + */ +int hv_getSize(HeavyContextInterface *c); + +/** Returns the sample rate with which this context has been configured. */ +double hv_getSampleRate(HeavyContextInterface *c); + +/** Returns the number of input channels with which this context has been configured. */ +int hv_getNumInputChannels(HeavyContextInterface *c); + +/** Returns the number of output channels with which this context has been configured. */ +int hv_getNumOutputChannels(HeavyContextInterface *c); + +/** Set the print hook. The function is called whenever a message is sent to a print object. */ +void hv_setPrintHook(HeavyContextInterface *c, HvPrintHook_t *f); + +/** Returns the print hook, or NULL. */ +HvPrintHook_t *hv_getPrintHook(HeavyContextInterface *c); + +/** + * Set the send hook. The function is called whenever a message is sent to any send object. + * Messages returned by this function should NEVER be freed. If the message must persist, call + * hv_msg_copy() first. + */ +void hv_setSendHook(HeavyContextInterface *c, HvSendHook_t *f); + +/** Returns a 32-bit hash of any string. Returns 0 if string is NULL. */ +hv_uint32_t hv_stringToHash(const char *s); + +/** + * A convenience function to send a bang to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendBangToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash); + +/** + * A convenience function to send a float to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendFloatToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, const float x); + +/** + * A convenience function to send a symbol to a receiver to be processed immediately. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendSymbolToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, char *s); + +/** + * Sends a formatted message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendMessageToReceiverV(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, const char *format, ...); + +/** + * Sends a message to a receiver that can be scheduled for the future. + * The receiver is addressed with its hash, which can also be determined using hv_stringToHash(). + * This function is thread-safe. + * + * @return True if the message was accepted. False if the message could not fit onto + * the message queue to be processed this block. + */ +bool hv_sendMessageToReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, HvMessage *m); + +/** + * Cancels a previously scheduled message. + * + * @param sendMessage May be NULL. + */ +void hv_cancelMessage(HeavyContextInterface *c, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Returns the read-only user-assigned name of this patch. */ +const char *hv_getName(HeavyContextInterface *c); + +/** Sets a user-definable value. This value is never manipulated by Heavy. */ +void hv_setUserData(HeavyContextInterface *c, void *userData); + +/** Returns the user-defined data. */ +void *hv_getUserData(HeavyContextInterface *c); + +/** Returns the current patch time in milliseconds. This value may have rounding errors. */ +double hv_getCurrentTime(HeavyContextInterface *c); + +/** Returns the current patch time in samples. This value is always exact. */ +hv_uint32_t hv_getCurrentSample(HeavyContextInterface *c); + +/** + * Returns information about each parameter such as name, hash, and range. + * The total number of parameters is always returned. + * + * @param index The parameter index. + * @param info A pointer to a HvParameterInfo struct. May be null. + * + * @return The total number of parameters. + */ +int hv_getParameterInfo(HeavyContextInterface *c, int index, HvParameterInfo *info); + +/** */ +float hv_samplesToMilliseconds(HeavyContextInterface *c, hv_uint32_t numSamples); + +/** Converts milliseconds to samples. Input is limited to non-negative range. */ +hv_uint32_t hv_millisecondsToSamples(HeavyContextInterface *c, float ms); + +/** + * Acquire the input message queue lock. + * + * This function will block until the message lock as been acquired. + * Typical applications will not require the use of this function. + * + * @param c A Heavy context. + */ +void hv_lock_acquire(HeavyContextInterface *c); + +/** + * Try to acquire the input message queue lock. + * + * If the lock has been acquired, hv_lock_release() must be called to release it. + * Typical applications will not require the use of this function. + * + * @param c A Heavy context. + * + * @return Returns true if the lock has been acquired, false otherwise. + */ +bool hv_lock_try(HeavyContextInterface *c); + +/** + * Release the input message queue lock. + * + * Typical applications will not require the use of this function. + * + * @param c A Heavy context. + */ +void hv_lock_release(HeavyContextInterface *c); + +/** + * Set the size of the input message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * + * @param c A Heavy context. + * @param inQueueKb Must be positive i.e. at least one. + */ +void hv_setInputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t inQueueKb); + +/** + * Set the size of the output message queue in kilobytes. + * + * The buffer is reset and all existing contents are lost on resize. + * Only the default sendhook uses the outgoing message queue. If the default + * sendhook is not being used, then this function is not useful. + * + * @param c A Heavy context. + * @param outQueueKb Must be postive i.e. at least one. + */ +void hv_setOutputMessageQueueSize(HeavyContextInterface *c, hv_uint32_t outQueueKb); + +/** + * Get the next message in the outgoing queue, will also consume the message. + * Returns false if there are no messages. + * + * @param c A Heavy context. + * @param destinationHash a hash of the name of the receiver the message was sent to. + * @param outMsg message pointer that is filled by the next message contents. + * @param msgLength length of outMsg in bytes. + * + * @return True if there is a message in the outgoing queue. +*/ +bool hv_getNextSentMessage(HeavyContextInterface *c, hv_uint32_t *destinationHash, HvMessage *outMsg, hv_uint32_t msgLength); + + + +#if HV_APPLE +#pragma mark - Heavy Message +#endif + +typedef struct HvMessage HvMessage; + +/** Returns the total size in bytes of a HvMessage with a number of elements on the heap. */ +unsigned long hv_msg_getByteSize(hv_uint32_t numElements); + +/** Initialise a HvMessage structure with the number of elements and a timestamp (in samples). */ +void hv_msg_init(HvMessage *m, int numElements, hv_uint32_t timestamp); + +/** Returns the number of elements in this message. */ +unsigned long hv_msg_getNumElements(const HvMessage *m); + +/** Returns the time at which this message exists (in samples). */ +hv_uint32_t hv_msg_getTimestamp(const HvMessage *m); + +/** Set the time at which this message should be executed (in samples). */ +void hv_msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp); + +/** Returns true of the indexed element is a bang. False otherwise. Index is not bounds checked. */ +bool hv_msg_isBang(const HvMessage *const m, int i); + +/** Sets the indexed element to a bang. Index is not bounds checked. */ +void hv_msg_setBang(HvMessage *m, int i); + +/** Returns true of the indexed element is a float. False otherwise. Index is not bounds checked. */ +bool hv_msg_isFloat(const HvMessage *const m, int i); + +/** Returns the indexed element as a float value. Index is not bounds checked. */ +float hv_msg_getFloat(const HvMessage *const m, int i); + +/** Sets the indexed element to float value. Index is not bounds checked. */ +void hv_msg_setFloat(HvMessage *m, int i, float f); + +/** Returns true of the indexed element is a symbol. False otherwise. Index is not bounds checked. */ +bool hv_msg_isSymbol(const HvMessage *const m, int i); + +/** Returns the indexed element as a symbol value. Index is not bounds checked. */ +const char *hv_msg_getSymbol(const HvMessage *const m, int i); + +/** Returns true of the indexed element is a hash. False otherwise. Index is not bounds checked. */ +bool hv_msg_isHash(const HvMessage *const m, int i); + +/** Returns the indexed element as a hash value. Index is not bounds checked. */ +hv_uint32_t hv_msg_getHash(const HvMessage *const m, int i); + +/** Sets the indexed element to symbol value. Index is not bounds checked. */ +void hv_msg_setSymbol(HvMessage *m, int i, const char *s); + +/** + * Returns true if the message has the given format, in number of elements and type. False otherwise. + * Valid element types are: + * 'b': bang + * 'f': float + * 's': symbol + * + * For example, a message with three floats would have a format of "fff". A single bang is "b". + * A message with two symbols is "ss". These types can be mixed and matched in any way. + */ +bool hv_msg_hasFormat(const HvMessage *const m, const char *fmt); + +/** + * Returns a basic string representation of the message. + * The character array MUST be deallocated by the caller. + */ +char *hv_msg_toString(const HvMessage *const m); + +/** Copy a message onto the stack. The message persists. */ +HvMessage *hv_msg_copy(const HvMessage *const m); + +/** Free a copied message. */ +void hv_msg_free(HvMessage *m); + + + +#if HV_APPLE +#pragma mark - Heavy Table +#endif + +/** + * Resizes the table to the given length. + * + * Existing contents are copied to the new table. Remaining space is cleared + * if the table is longer than the original, truncated otherwise. + * + * @param tableHash The table identifier. + * @param newSampleLength The new length of the table, in samples. Must be positive. + * + * @return False if the table could not be found. True otherwise. + */ +bool hv_table_setLength(HeavyContextInterface *c, hv_uint32_t tableHash, hv_uint32_t newSampleLength); + +/** Returns a pointer to the raw buffer backing this table. DO NOT free it. */ +float *hv_table_getBuffer(HeavyContextInterface *c, hv_uint32_t tableHash); + +/** Returns the length of this table in samples. */ +hv_uint32_t hv_table_getLength(HeavyContextInterface *c, hv_uint32_t tableHash); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_H_ diff --git a/delay_simple/plugin/source/HvHeavyInternal.h b/delay_simple/plugin/source/HvHeavyInternal.h new file mode 100644 index 0000000..e10b944 --- /dev/null +++ b/delay_simple/plugin/source/HvHeavyInternal.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_INTERNAL_H_ +#define _HEAVY_INTERNAL_H_ + +#include "HvHeavy.h" +#include "HvUtils.h" +#include "HvTable.h" +#include "HvMessage.h" +#include "HvMath.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + */ +HvTable *hv_table_get(HeavyContextInterface *c, hv_uint32_t tableHash); + +/** + * + */ +void hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m); + +/** + * + */ +HvMessage *hv_scheduleMessageForObject(HeavyContextInterface *c, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *), + int letIndex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/delay_simple/plugin/source/HvLightPipe.c b/delay_simple/plugin/source/HvLightPipe.c new file mode 100644 index 0000000..98de428 --- /dev/null +++ b/delay_simple/plugin/source/HvLightPipe.c @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvLightPipe.h" + +#if __SSE__ || HV_SIMD_SSE +#include +#define hv_sfence() _mm_sfence() +#elif __arm__ || HV_SIMD_NEON + #if __ARM_ACLE + #include + // https://msdn.microsoft.com/en-us/library/hh875058.aspx#BarrierRestrictions + // http://doxygen.reactos.org/d8/d47/armintr_8h_a02be7ec76ca51842bc90d9b466b54752.html + #define hv_sfence() __dmb(0xE) /* _ARM_BARRIER_ST */ + #elif defined(__GNUC__) + #define hv_sfence() __asm__ volatile ("dmb 0xE":::"memory") + #else + // http://stackoverflow.com/questions/19965076/gcc-memory-barrier-sync-synchronize-vs-asm-volatile-memory + #define hv_sfence() __sync_synchronize() + #endif +#elif HV_WIN +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684208(v=vs.85).aspx +#define hv_sfence() _WriteBarrier() +#else +#define hv_sfence() __asm__ volatile("" : : : "memory") +#endif + +#define HLP_STOP 0 +#define HLP_LOOP 0xFFFFFFFF +#define HLP_SET_UINT32_AT_BUFFER(a, b) (*((hv_uint32_t *) (a)) = (b)) +#define HLP_GET_UINT32_AT_BUFFER(a) (*((hv_uint32_t *) (a))) + +hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes) { + if (numBytes > 0) { + q->buffer = (char *) hv_malloc(numBytes); + hv_assert(q->buffer != NULL); + HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); + } else { + q->buffer = NULL; + } + q->writeHead = q->buffer; + q->readHead = q->buffer; + q->len = numBytes; + q->remainingBytes = numBytes; + return numBytes; +} + +void hLp_free(HvLightPipe *q) { + hv_free(q->buffer); +} + +hv_uint32_t hLp_hasData(HvLightPipe *q) { + hv_uint32_t x = HLP_GET_UINT32_AT_BUFFER(q->readHead); + if (x == HLP_LOOP) { + q->readHead = q->buffer; + x = HLP_GET_UINT32_AT_BUFFER(q->readHead); + } + return x; +} + +char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t bytesToWrite) { + char *const readHead = q->readHead; + char *const oldWriteHead = q->writeHead; + const hv_uint32_t totalByteRequirement = bytesToWrite + 2*sizeof(hv_uint32_t); + + // check if there is enough space to write the data in the remaining + // length of the buffer + if (totalByteRequirement <= q->remainingBytes) { + char *const newWriteHead = oldWriteHead + sizeof(hv_uint32_t) + bytesToWrite; + + // check if writing would overwrite existing data in the pipe (return NULL if so) + if ((oldWriteHead < readHead) && (newWriteHead >= readHead)) return NULL; + else return (oldWriteHead + sizeof(hv_uint32_t)); + } else { + // there isn't enough space, try looping around to the start + if (totalByteRequirement <= q->len) { + if ((oldWriteHead < readHead) || ((q->buffer + totalByteRequirement) > readHead)) { + return NULL; // overwrite condition + } else { + q->writeHead = q->buffer; + q->remainingBytes = q->len; + HLP_SET_UINT32_AT_BUFFER(q->buffer, HLP_STOP); + hv_sfence(); + HLP_SET_UINT32_AT_BUFFER(oldWriteHead, HLP_LOOP); + return q->buffer + sizeof(hv_uint32_t); + } + } else { + return NULL; // there isn't enough space to write the data + } + } +} + +void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes) { + hv_assert(q->remainingBytes >= (numBytes + 2*sizeof(hv_uint32_t))); + q->remainingBytes -= (sizeof(hv_uint32_t) + numBytes); + char *const oldWriteHead = q->writeHead; + q->writeHead += (sizeof(hv_uint32_t) + numBytes); + HLP_SET_UINT32_AT_BUFFER(q->writeHead, HLP_STOP); + + // save everything before this point to memory + hv_sfence(); + + // then save this + HLP_SET_UINT32_AT_BUFFER(oldWriteHead, numBytes); +} + +char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes) { + *numBytes = HLP_GET_UINT32_AT_BUFFER(q->readHead); + char *const readBuffer = q->readHead + sizeof(hv_uint32_t); + return readBuffer; +} + +void hLp_consume(HvLightPipe *q) { + hv_assert(HLP_GET_UINT32_AT_BUFFER(q->readHead) != HLP_STOP); + q->readHead += sizeof(hv_uint32_t) + HLP_GET_UINT32_AT_BUFFER(q->readHead); +} + +void hLp_reset(HvLightPipe *q) { + q->writeHead = q->buffer; + q->readHead = q->buffer; + q->remainingBytes = q->len; + memset(q->buffer, 0, q->len); +} diff --git a/delay_simple/plugin/source/HvLightPipe.h b/delay_simple/plugin/source/HvLightPipe.h new file mode 100644 index 0000000..438f726 --- /dev/null +++ b/delay_simple/plugin/source/HvLightPipe.h @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_LIGHTPIPE_H_ +#define _HEAVY_LIGHTPIPE_H_ + +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This pipe assumes that there is only one producer thread and one consumer + * thread. This data structure does not support any other configuration. + */ +typedef struct HvLightPipe { + char *buffer; + char *writeHead; + char *readHead; + hv_uint32_t len; + hv_uint32_t remainingBytes; // total bytes from write head to end +} HvLightPipe; + +/** + * Initialise the pipe with a given length, in bytes. + * @return Returns the size of the pipe in bytes. + */ +hv_uint32_t hLp_init(HvLightPipe *q, hv_uint32_t numBytes); + +/** + * Frees the internal buffer. + * @param q The light pipe. + */ +void hLp_free(HvLightPipe *q); + +/** + * Indicates if data is available for reading. + * @param q The light pipe. + * + * @return Returns the number of bytes available for reading. Zero if no bytes + * are available. + */ +hv_uint32_t hLp_hasData(HvLightPipe *q); + +/** + * Returns a pointer to a location in the pipe where numBytes can be written. + * + * @param numBytes The number of bytes to be written. + * @return A pointer to a location where those bytes can be written. Returns + * NULL if no more space is available. Successive calls to this + * function may eventually return a valid pointer because the readhead + * has been advanced on another thread. + */ +char *hLp_getWriteBuffer(HvLightPipe *q, hv_uint32_t numBytes); + +/** + * Indicates to the pipe how many bytes have been written. + * + * @param numBytes The number of bytes written. In general this should be the + * same value as was passed to the preceeding call to + * hLp_getWriteBuffer(). + */ +void hLp_produce(HvLightPipe *q, hv_uint32_t numBytes); + +/** + * Returns the current read buffer, indicating the number of bytes available + * for reading. + * @param q The light pipe. + * @param numBytes This value will be filled with the number of bytes available + * for reading. + * + * @return A pointer to the read buffer. + */ +char *hLp_getReadBuffer(HvLightPipe *q, hv_uint32_t *numBytes); + +/** + * Indicates that the next set of bytes have been read and are no longer needed. + * @param q The light pipe. + */ +void hLp_consume(HvLightPipe *q); + +// resets the queue to it's initialised state +// This should be done when only one thread is accessing the pipe. +void hLp_reset(HvLightPipe *q); + +#ifdef __cplusplus +} +#endif + +#endif // _HEAVY_LIGHTPIPE_H_ diff --git a/delay_simple/plugin/source/HvMath.h b/delay_simple/plugin/source/HvMath.h new file mode 100644 index 0000000..8bdcd6b --- /dev/null +++ b/delay_simple/plugin/source/HvMath.h @@ -0,0 +1,724 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_MATH_H_ +#define _HEAVY_MATH_H_ + +#include "HvUtils.h" + +// https://software.intel.com/sites/landingpage/IntrinsicsGuide/ +// https://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/ARM-NEON-Intrinsics.html +// http://codesuppository.blogspot.co.uk/2015/02/sse2neonh-porting-guide-and-header-file.html + +static inline void __hv_zero_f(hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_setzero_ps(); +#elif HV_SIMD_SSE + *bOut = _mm_setzero_ps(); +#elif HV_SIMD_NEON + *bOut = vdupq_n_f32(0.0f); +#else // HV_SIMD_NONE + *bOut = 0.0f; +#endif +} + +static inline void __hv_zero_i(hv_bOuti_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_setzero_si256(); +#elif HV_SIMD_SSE + *bOut = _mm_setzero_si128(); +#elif HV_SIMD_NEON + *bOut = vdupq_n_s32(0); +#else // HV_SIMD_NONE + *bOut = 0; +#endif +} + +static inline void __hv_load_f(float *bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_load_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_load_ps(bIn); +#elif HV_SIMD_NEON + *bOut = vld1q_f32(bIn); +#else // HV_SIMD_NONE + *bOut = *bIn; +#endif +} + +static inline void __hv_store_f(float *bOut, hv_bInf_t bIn) { +#if HV_SIMD_AVX + _mm256_store_ps(bOut, bIn); +#elif HV_SIMD_SSE + _mm_store_ps(bOut, bIn); +#elif HV_SIMD_NEON + vst1q_f32(bOut, bIn); +#else // HV_SIMD_NONE + *bOut = bIn; +#endif +} + +static inline void __hv_log2_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_log2_f() not implemented +#elif HV_SIMD_SSE + // https://en.wikipedia.org/wiki/Fast_inverse_square_root + __m128i a = _mm_castps_si128(bIn); + __m128i b = _mm_srli_epi32(a, 23); + __m128i c = _mm_sub_epi32(b, _mm_set1_epi32(127)); // exponent (int) + __m128 d = _mm_cvtepi32_ps(c); // exponent (float) + __m128i e = _mm_or_si128(_mm_andnot_si128(_mm_set1_epi32(0xFF800000), a), _mm_set1_epi32(0x3F800000)); + __m128 f = _mm_castsi128_ps(e); // 1+m (float) + __m128 g = _mm_add_ps(d, f); // e + 1 + m + __m128 h = _mm_add_ps(g, _mm_set1_ps(-0.9569643f)); // e + 1 + m + (sigma-1) + *bOut = h; +#elif HV_SIMD_NEON + int32x4_t a = vreinterpretq_s32_f32(bIn); + int32x4_t b = vshrq_n_s32(a, 23); + int32x4_t c = vsubq_s32(b, vdupq_n_s32(127)); + float32x4_t d = vcvtq_f32_s32(c); + int32x4_t e = vorrq_s32(vbicq_s32(a, vdupq_n_s32(0xFF800000)), vdupq_n_s32(0x3F800000)); + float32x4_t f = vreinterpretq_f32_s32(e); + float32x4_t g = vaddq_f32(d, f); + float32x4_t h = vaddq_f32(g, vdupq_n_f32(-0.9569643f)); + *bOut = h; +#else // HV_SIMD_NONE + *bOut = 1.442695040888963f * hv_log_f(bIn); +#endif +} + +// NOTE(mhroth): this is a pretty ghetto implementation +static inline void __hv_cos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_set_ps( + hv_cos_f(bIn[7]), hv_cos_f(bIn[6]), hv_cos_f(bIn[5]), hv_cos_f(bIn[4]), + hv_cos_f(bIn[3]), hv_cos_f(bIn[2]), hv_cos_f(bIn[1]), hv_cos_f(bIn[0])); +#elif HV_SIMD_SSE + const float *const b = (float *) &bIn; + *bOut = _mm_set_ps(hv_cos_f(b[3]), hv_cos_f(b[2]), hv_cos_f(b[1]), hv_cos_f(b[0])); +#elif HV_SIMD_NEON + *bOut = (float32x4_t) {hv_cos_f(bIn[0]), hv_cos_f(bIn[1]), hv_cos_f(bIn[2]), hv_cos_f(bIn[3])}; +#else // HV_SIMD_NONE + *bOut = hv_cos_f(bIn); +#endif +} + +static inline void __hv_acos_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_acos_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_acos_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_acos_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_acos_f(bIn); +#endif +} + +static inline void __hv_cosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_cosh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_cosh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_cosh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_cosh_f(bIn); +#endif +} + +static inline void __hv_acosh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_acosh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_acosh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_acosh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_acosh_f(bIn); +#endif +} + +static inline void __hv_sin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_sin_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_sin_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_sin_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_sin_f(bIn); +#endif +} + +static inline void __hv_asin_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_asin_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_asin_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_asin_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_asin_f(bIn); +#endif +} + +static inline void __hv_sinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_sinh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_sinh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_sinh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_sinh_f(bIn); +#endif +} + +static inline void __hv_asinh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_asinh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_asinh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_asinh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_asinh_f(bIn); +#endif +} + +static inline void __hv_tan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_tan_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_tan_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_tan_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_tan_f(bIn); +#endif +} + +static inline void __hv_atan_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_atan_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_atan_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_atan_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_atan_f(bIn); +#endif +} + +static inline void __hv_atan2_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_atan2_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_atan2_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_atan2_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_atan2_f(bIn0, bIn1); +#endif +} + +static inline void __hv_tanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_tanh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_tanh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_tanh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_tanh_f(bIn); +#endif +} + +static inline void __hv_atanh_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + hv_assert(0); // __hv_atanh_f() not implemented +#elif HV_SIMD_SSE + hv_assert(0); // __hv_atanh_f() not implemented +#elif HV_SIMD_NEON + hv_assert(0); // __hv_atanh_f() not implemented +#else // HV_SIMD_NONE + *bOut = hv_atanh_f(bIn); +#endif +} + +static inline void __hv_sqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_sqrt_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_sqrt_ps(bIn); +#elif HV_SIMD_NEON + const float32x4_t y = vrsqrteq_f32(bIn); + *bOut = vmulq_f32(bIn, vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y)); // numerical results may be inexact +#else // HV_SIMD_NONE + *bOut = hv_sqrt_f(bIn); +#endif +} + +static inline void __hv_rsqrt_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_rsqrt_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_rsqrt_ps(bIn); +#elif HV_SIMD_NEON + const float32x4_t y = vrsqrteq_f32(bIn); + *bOut = vmulq_f32(vrsqrtsq_f32(vmulq_f32(bIn, y), y), y); // numerical results may be inexact +#else // HV_SIMD_NONE + *bOut = 1.0f/hv_sqrt_f(bIn); +#endif +} + +static inline void __hv_abs_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_andnot_ps(_mm256_set1_ps(-0.0f), bIn); +#elif HV_SIMD_SSE + *bOut = _mm_andnot_ps(_mm_set1_ps(-0.0f), bIn); // == 1 << 31 +#elif HV_SIMD_NEON + *bOut = vabsq_f32(bIn); +#else // HV_SIMD_NONE + *bOut = hv_abs_f(bIn); +#endif +} + +static inline void __hv_neg_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_xor_ps(bIn, _mm256_set1_ps(-0.0f)); +#elif HV_SIMD_SSE + *bOut = _mm_xor_ps(bIn, _mm_set1_ps(-0.0f)); +#elif HV_SIMD_NEON + *bOut = vnegq_f32(bIn); +#else // HV_SIMD_NONE + *bOut = bIn * -1.0f; +#endif +} + +static inline void __hv_exp_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); + _mm256_store_ps(b, bIn); + *bOut = _mm256_set_ps( + hv_exp_f(b[7]), hv_exp_f(b[6]), hv_exp_f(b[5]), hv_exp_f(b[4]), + hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); +#elif HV_SIMD_SSE + float *const b = (float *) hv_alloca(HV_N_SIMD*sizeof(float)); + _mm_store_ps(b, bIn); + *bOut = _mm_set_ps(hv_exp_f(b[3]), hv_exp_f(b[2]), hv_exp_f(b[1]), hv_exp_f(b[0])); +#elif HV_SIMD_NEON + *bOut = (float32x4_t) { + hv_exp_f(bIn[0]), + hv_exp_f(bIn[1]), + hv_exp_f(bIn[2]), + hv_exp_f(bIn[3])}; +#else // HV_SIMD_NONE + *bOut = hv_exp_f(bIn); +#endif +} + +static inline void __hv_ceil_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_ceil_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_ceil_ps(bIn); +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vrndpq_f32(bIn); +#else + // A slow NEON implementation of __hv_ceil_f() is being used because + // the necessary intrinsic cannot be found. It is only available in ARMv8. + *bOut = (float32x4_t) {hv_ceil_f(bIn[0]), hv_ceil_f(bIn[1]), hv_ceil_f(bIn[2]), hv_ceil_f(bIn[3])}; +#endif // vrndpq_f32 +#else // HV_SIMD_NONE + *bOut = hv_ceil_f(bIn); +#endif +} + +static inline void __hv_floor_f(hv_bInf_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_floor_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_floor_ps(bIn); +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vrndmq_f32(bIn); +#else + // A slow implementation of __hv_floor_f() is being used because + // the necessary intrinsic cannot be found. It is only available from ARMv8. + *bOut = (float32x4_t) {hv_floor_f(bIn[0]), hv_floor_f(bIn[1]), hv_floor_f(bIn[2]), hv_floor_f(bIn[3])}; +#endif // vrndmq_f32 +#else // HV_SIMD_NONE + *bOut = hv_floor_f(bIn); +#endif +} + +// __add~f +static inline void __hv_add_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_add_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_add_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vaddq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 + bIn1; +#endif +} + +// __add~i +static inline void __hv_add_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_add_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_add_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_add_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vaddq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 + bIn1; +#endif +} + +// __sub~f +static inline void __hv_sub_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_sub_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_sub_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vsubq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 - bIn1; +#endif +} + +// __mul~f +static inline void __hv_mul_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_mul_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_mul_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmulq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 * bIn1; +#endif +} + +// __*~i +static inline void __hv_mul_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_mullo_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_mullo_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_mullo_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmulq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = bIn0 * bIn1; +#endif +} + +// __cast~if +static inline void __hv_cast_if(hv_bIni_t bIn, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cvtepi32_ps(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_cvtepi32_ps(bIn); +#elif HV_SIMD_NEON + *bOut = vcvtq_f32_s32(bIn); +#else // HV_SIMD_NONE + *bOut = (float) bIn; +#endif +} + +// __cast~fi +static inline void __hv_cast_fi(hv_bInf_t bIn, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cvtps_epi32(bIn); +#elif HV_SIMD_SSE + *bOut = _mm_cvtps_epi32(bIn); +#elif HV_SIMD_NEON + *bOut = vcvtq_s32_f32(bIn); +#else // HV_SIMD_NONE + *bOut = (int) bIn; +#endif +} + +static inline void __hv_div_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + __m256 a = _mm256_cmp_ps(bIn1, _mm256_setzero_ps(), _CMP_EQ_OQ); + __m256 b = _mm256_div_ps(bIn0, bIn1); + *bOut = _mm256_andnot_ps(a, b); +#elif HV_SIMD_SSE + __m128 a = _mm_cmpeq_ps(bIn1, _mm_setzero_ps()); + __m128 b = _mm_div_ps(bIn0, bIn1); + *bOut = _mm_andnot_ps(a, b); +#elif HV_SIMD_NEON + uint32x4_t a = vceqq_f32(bIn1, vdupq_n_f32(0.0f)); + float32x4_t b = vmulq_f32(bIn0, vrecpeq_f32(bIn1)); // NOTE(mhroth): numerical results may be inexact + *bOut = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(b), a)); +#else // HV_SIMD_NONE + *bOut = (bIn1 != 0.0f) ? (bIn0 / bIn1) : 0.0f; +#endif +} + +static inline void __hv_min_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_min_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_min_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vminq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_min_f(bIn0, bIn1); +#endif +} + +static inline void __hv_min_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_min_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_min_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_min_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vminq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_min_i(bIn0, bIn1); +#endif +} + +static inline void __hv_max_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_max_ps(bIn0, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_max_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmaxq_f32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_max_f(bIn0, bIn1); +#endif +} + +static inline void __hv_max_i(hv_bIni_t bIn0, hv_bIni_t bIn1, hv_bOuti_t bOut) { +#if HV_SIMD_AVX + __m128i x = _mm_max_epi32(_mm256_castsi256_si128(bIn0), _mm256_castsi256_si128(bIn1)); + __m128i y = _mm_max_epi32(_mm256_extractf128_si256(bIn0, 1), _mm256_extractf128_si256(bIn1, 1)); + *bOut = _mm256_insertf128_si256(_mm256_castsi128_si256(x), y, 1); +#elif HV_SIMD_SSE + *bOut = _mm_max_epi32(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vmaxq_s32(bIn0, bIn1); +#else // HV_SIMD_NONE + *bOut = hv_max_i(bIn0, bIn1); +#endif +} + +static inline void __hv_pow_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + float *b = (float *) hv_alloca(16*sizeof(float)); + _mm256_store_ps(b, bIn0); + _mm256_store_ps(b+8, bIn1); + *bOut = _mm256_set_ps( + hv_pow_f(b[7], b[7]), + hv_pow_f(b[6], b[6]), + hv_pow_f(b[5], b[5]), + hv_pow_f(b[4], b[4]), + hv_pow_f(b[3], b[3]), + hv_pow_f(b[2], b[2]), + hv_pow_f(b[1], b[1]), + hv_pow_f(b[0], b[0])); +#elif HV_SIMD_SSE + float *b = (float *) hv_alloca(8*sizeof(float)); + _mm_store_ps(b, bIn0); + _mm_store_ps(b+4, bIn1); + *bOut = _mm_set_ps( + hv_pow_f(b[3], b[7]), + hv_pow_f(b[2], b[6]), + hv_pow_f(b[1], b[5]), + hv_pow_f(b[0], b[4])); +#elif HV_SIMD_NEON + *bOut = (float32x4_t) { + hv_pow_f(bIn0[0], bIn1[0]), + hv_pow_f(bIn0[1], bIn1[1]), + hv_pow_f(bIn0[2], bIn1[2]), + hv_pow_f(bIn0[3], bIn1[3])}; +#else // HV_SIMD_NONE + *bOut = hv_pow_f(bIn0, bIn1); +#endif +} + +static inline void __hv_gt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GT_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmpgt_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcgtq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 > bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_gte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_GE_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmpge_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcgeq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 >= bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_lt_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LT_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmplt_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcltq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 < bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_lte_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_LE_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmple_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vcleq_f32(bIn0, bIn1)); +#else // HV_SIMD_NONE + *bOut = (bIn0 <= bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_neq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_NEQ_OQ); +#elif HV_SIMD_SSE + *bOut = _mm_cmpneq_ps(bIn0, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(bIn0, bIn1))); +#else // HV_SIMD_NONE + *bOut = (bIn0 != bIn1) ? 1.0f : 0.0f; +#endif +} + +static inline void __hv_or_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_or_ps(bIn1, bIn0); +#elif HV_SIMD_SSE + *bOut = _mm_or_ps(bIn1, bIn0); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); +#else // HV_SIMD_NONE + if (bIn0 == 0.0f && bIn1 == 0.0f) *bOut = 0.0f; + else if (bIn0 == 0.0f) *bOut = bIn1; + else if (bIn1 == 0.0f) *bOut = bIn0; + else hv_assert(0); +#endif +} + +static inline void __hv_and_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_and_ps(bIn1, bIn0); +#elif HV_SIMD_SSE + *bOut = _mm_and_ps(bIn1, bIn0); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(bIn1), vreinterpretq_u32_f32(bIn0))); +#else // HV_SIMD_NONE + if (bIn0 == 0.0f || bIn1 == 0.0f) *bOut = 0.0f; + else if (bIn0 == 1.0f) *bOut = bIn1; + else if (bIn1 == 1.0f) *bOut = bIn0; + else hv_assert(0); +#endif +} + +static inline void __hv_andnot_f(hv_bInf_t bIn0_mask, hv_bInf_t bIn1, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_andnot_ps(bIn0_mask, bIn1); +#elif HV_SIMD_SSE + *bOut = _mm_andnot_ps(bIn0_mask, bIn1); +#elif HV_SIMD_NEON + *bOut = vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(bIn1), vreinterpretq_s32_f32(bIn0_mask))); +#else // HV_SIMD_NONE + *bOut = (bIn0_mask == 0.0f) ? bIn1 : 0.0f; +#endif +} + +// bOut = (bIn0 * bIn1) + bIn2 +static inline void __hv_fma_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { +#if HV_SIMD_AVX +#if HV_SIMD_FMA + *bOut = _mm256_fmadd_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm256_add_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_SSE +#if HV_SIMD_FMA + *bOut = _mm_fmadd_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm_add_ps(_mm_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vfmaq_f32(bIn2, bIn0, bIn1); +#else + // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures + *bOut = vaddq_f32(vmulq_f32(bIn0, bIn1), bIn2); +#endif +#else // HV_SIMD_NONE + *bOut = hv_fma_f(bIn0, bIn1, bIn2); +#endif +} + +// bOut = (bIn0 * bIn1) - bIn2 +static inline void __hv_fms_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bInf_t bIn2, hv_bOutf_t bOut) { +#if HV_SIMD_AVX +#if HV_SIMD_FMA + *bOut = _mm256_fmsub_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm256_sub_ps(_mm256_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_SSE +#if HV_SIMD_FMA + *bOut = _mm_fmsub_ps(bIn0, bIn1, bIn2); +#else + *bOut = _mm_sub_ps(_mm_mul_ps(bIn0, bIn1), bIn2); +#endif // HV_SIMD_FMA +#elif HV_SIMD_NEON +#if __ARM_ARCH >= 8 + *bOut = vfmsq_f32(bIn2, bIn0, bIn1); +#else + // NOTE(mhroth): it turns out, fma SUUUUCKS on lesser ARM architectures + *bOut = vsubq_f32(vmulq_f32(bIn0, bIn1), bIn2); +#endif +#else // HV_SIMD_NONE + *bOut = (bIn0 * bIn1) - bIn2; +#endif +} + +#endif // _HEAVY_MATH_H_ diff --git a/delay_simple/plugin/source/HvMessage.c b/delay_simple/plugin/source/HvMessage.c new file mode 100644 index 0000000..0f1ac5d --- /dev/null +++ b/delay_simple/plugin/source/HvMessage.c @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvMessage.h" + +HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) { + m->timestamp = timestamp; + m->numElements = (hv_uint16_t) numElements; + m->numBytes = (hv_uint16_t) msg_getCoreSize(numElements); + return m; +} + +HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage); + msg_setFloat(m, 0, f); + return m; +} + +HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage); + msg_setBang(m, 0); + return m; +} + +HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage) + (hv_uint16_t) hv_strlen(s); + msg_setSymbol(m, 0, s); + return m; +} + +HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) { + m->timestamp = timestamp; + m->numElements = 1; + m->numBytes = sizeof(HvMessage); + msg_setHash(m, 0, h); + return m; +} + +void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) { + HvMessage *r = (HvMessage *) buffer; + + hv_size_t len_r = msg_getCoreSize(msg_getNumElements(m)); + + // assert that the message is not already larger than the length of the buffer + hv_assert(len_r <= len); + + // copy the basic message to the buffer + hv_memcpy(r, m, len_r); + + char *p = buffer + len_r; // points to the end of the base message + for (int i = 0; i < msg_getNumElements(m); ++i) { + if (msg_isSymbol(m,i)) { + const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char + hv_assert(len_r + symLen <= len); // stay safe! + hv_strncpy(p, msg_getSymbol(m,i), symLen); + msg_setSymbol(r, i, p); + p += symLen; + len_r += symLen; + } + } + + r->numBytes = (hv_uint16_t) len_r; // update the message size in memory +} + +// the message is serialised such that all symbol elements are placed in order at the end of the buffer +HvMessage *msg_copy(const HvMessage *m) { + const hv_uint32_t heapSize = msg_getSize(m); + char *r = (char *) hv_malloc(heapSize); + hv_assert(r != NULL); + msg_copyToBuffer(m, r, heapSize); + return (HvMessage *) r; +} + +void msg_free(HvMessage *m) { + hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message +} + +bool msg_hasFormat(const HvMessage *m, const char *fmt) { + hv_assert(fmt != NULL); + const int n = msg_getNumElements(m); + for (int i = 0; i < n; ++i) { + switch (fmt[i]) { + case 'b': if (!msg_isBang(m, i)) return false; break; + case 'f': if (!msg_isFloat(m, i)) return false; break; + case 'h': if (!msg_isHash(m, i)) return false; break; + case 's': if (!msg_isSymbol(m, i)) return false; break; + default: return false; + } + } + return (fmt[n] == '\0'); +} + +bool msg_compareSymbol(const HvMessage *m, int i, const char *s) { + switch (msg_getType(m,i)) { + case HV_MSG_SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s); + case HV_MSG_HASH: return (msg_getHash(m,i) == hv_string_to_hash(s)); + default: return false; + } +} + +bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) { + if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) { + if (msg_getType(m, i_m) == msg_getType(n, i_n)) { + switch (msg_getType(m, i_m)) { + case HV_MSG_BANG: return true; + case HV_MSG_FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n)); + case HV_MSG_SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n)); + case HV_MSG_HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n); + default: break; + } + } + } + return false; +} + +void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) { + switch (msg_getType(m, i_m)) { + case HV_MSG_BANG: msg_setBang(n, i_n); break; + case HV_MSG_FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break; + case HV_MSG_SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break; + case HV_MSG_HASH: msg_setHash(n, i_n, msg_getHash(m, i_m)); + default: break; + } +} + +hv_uint32_t msg_getHash(const HvMessage *const m, int i) { + hv_assert(i < msg_getNumElements(m)); // invalid index + switch (msg_getType(m,i)) { + case HV_MSG_BANG: return 0xFFFFFFFF; + case HV_MSG_FLOAT: { + float f = msg_getFloat(m,i); + return *((hv_uint32_t *) &f); + } + case HV_MSG_SYMBOL: return hv_string_to_hash(msg_getSymbol(m,i)); + case HV_MSG_HASH: return (&(m->elem)+i)->data.h; + default: return 0; + } +} + +char *msg_toString(const HvMessage *m) { + hv_assert(msg_getNumElements(m) > 0); + int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int)); + int size = 0; // the total length of our final buffer + + // loop through every element in our list of atoms + // first loop figures out how long our buffer should be + for (int i = 0; i < msg_getNumElements(m); i++) { + // length of our string is each atom plus a space, or \0 on the end + switch (msg_getType(m, i)) { + case HV_MSG_BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break; + case HV_MSG_FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break; + case HV_MSG_SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break; + case HV_MSG_HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break; + default: break; + } + size += len[i]; + } + + hv_assert(size > 0); + + // now we do the piecewise concatenation into our final string + // the final buffer we will pass back after concatenating all strings - user should free it + char *finalString = (char *) hv_malloc(size*sizeof(char)); + hv_assert(finalString != NULL); + int pos = 0; + for (int i = 0; i < msg_getNumElements(m); i++) { + // put a string representation of each atom into the final string + switch (msg_getType(m, i)) { + case HV_MSG_BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break; + case HV_MSG_FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break; + case HV_MSG_SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break; + case HV_MSG_HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break; + default: break; + } + pos += len[i]; + finalString[pos-1] = 32; // ASCII space + } + finalString[size-1] = '\0'; // ensure that the string is null terminated + return finalString; +} diff --git a/delay_simple/plugin/source/HvMessage.h b/delay_simple/plugin/source/HvMessage.h new file mode 100644 index 0000000..a3fdd1c --- /dev/null +++ b/delay_simple/plugin/source/HvMessage.h @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_MESSAGE_H_ +#define _HEAVY_MESSAGE_H_ + +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum ElementType { + HV_MSG_BANG = 0, + HV_MSG_FLOAT = 1, + HV_MSG_SYMBOL = 2, + HV_MSG_HASH = 3 +} ElementType; + +typedef struct Element { + ElementType type; + union { + float f; // float + const char *s; // symbol + hv_uint32_t h; // hash + } data; +} Element; + +typedef struct HvMessage { + hv_uint32_t timestamp; // the sample at which this message should be processed + hv_uint16_t numElements; + hv_uint16_t numBytes; // the total number of bytes that this message occupies in memory, including strings + Element elem; +} HvMessage; + +typedef struct ReceiverMessagePair { + hv_uint32_t receiverHash; + HvMessage msg; +} ReceiverMessagePair; + +#define HV_MESSAGE_ON_STACK(_x) (HvMessage *) hv_alloca(msg_getCoreSize(_x)) + +/** Returns the number of bytes that this message consumes in memory, not including strings. */ +static inline hv_size_t msg_getCoreSize(hv_size_t numElements) { + hv_assert(numElements > 0); + return sizeof(HvMessage) + ((numElements-1) * sizeof(Element)); +} + +HvMessage *msg_copy(const HvMessage *m); + +/** Copies the message into the given buffer. The buffer must be at least as large as msg_getNumHeapBytes(). */ +void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len); + +void msg_setElementToFrom(HvMessage *n, int indexN, const HvMessage *const m, int indexM); + +/** Frees a message on the heap. Does nothing if argument is NULL. */ +void msg_free(HvMessage *m); + +HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp); + +HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f); + +HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp); + +HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, const char *s); + +HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h); + +static inline hv_uint32_t msg_getTimestamp(const HvMessage *m) { + return m->timestamp; +} + +static inline void msg_setTimestamp(HvMessage *m, hv_uint32_t timestamp) { + m->timestamp = timestamp; +} + +static inline int msg_getNumElements(const HvMessage *m) { + return (int) m->numElements; +} + +/** Returns the total number of bytes this message consumes in memory. */ +static inline hv_uint32_t msg_getSize(const HvMessage *m) { + return m->numBytes; +} + +static inline ElementType msg_getType(const HvMessage *m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + return (&(m->elem)+index)->type; +} + +static inline void msg_setBang(HvMessage *m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + (&(m->elem)+index)->type = HV_MSG_BANG; + (&(m->elem)+index)->data.s = NULL; +} + +static inline bool msg_isBang(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_BANG) : false; +} + +static inline void msg_setFloat(HvMessage *m, int index, float f) { + hv_assert(index < msg_getNumElements(m)); // invalid index + (&(m->elem)+index)->type = HV_MSG_FLOAT; + (&(m->elem)+index)->data.f = f; +} + +static inline float msg_getFloat(const HvMessage *const m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + return (&(m->elem)+index)->data.f; +} + +static inline bool msg_isFloat(const HvMessage *const m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m,index) == HV_MSG_FLOAT) : false; +} + +static inline void msg_setHash(HvMessage *m, int index, hv_uint32_t h) { + hv_assert(index < msg_getNumElements(m)); // invalid index + (&(m->elem)+index)->type = HV_MSG_HASH; + (&(m->elem)+index)->data.h = h; +} + +static inline bool msg_isHash(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_HASH) : false; +} + +/** Returns true if the element is a hash or symbol. False otherwise. */ +static inline bool msg_isHashLike(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? ((msg_getType(m, index) == HV_MSG_HASH) || (msg_getType(m, index) == HV_MSG_SYMBOL)) : false; +} + +/** Returns a 32-bit hash of the given element. */ +hv_uint32_t msg_getHash(const HvMessage *const m, int i); + +static inline void msg_setSymbol(HvMessage *m, int index, const char *s) { + hv_assert(index < msg_getNumElements(m)); // invalid index + hv_assert(s != NULL); + (&(m->elem)+index)->type = HV_MSG_SYMBOL; + (&(m->elem)+index)->data.s = s; + // NOTE(mhroth): if the same message container is reused and string reset, + // then the message size will be overcounted + m->numBytes += (hv_uint16_t) (hv_strlen(s) + 1); // also count '\0' +} + +static inline const char *msg_getSymbol(const HvMessage *m, int index) { + hv_assert(index < msg_getNumElements(m)); // invalid index + return (&(m->elem)+index)->data.s; +} + +static inline bool msg_isSymbol(const HvMessage *m, int index) { + return (index < msg_getNumElements(m)) ? (msg_getType(m, index) == HV_MSG_SYMBOL) : false; +} + +bool msg_compareSymbol(const HvMessage *m, int i, const char *s); + +/** Returns 1 if the element i_m of message m is equal to element i_n of message n. */ +bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n); + +bool msg_hasFormat(const HvMessage *m, const char *fmt); + +/** + * Create a string representation of the message. Suitable for use by the print object. + * The resulting string must be freed by the caller. + */ +char *msg_toString(const HvMessage *msg); + +#ifdef __cplusplus +} +#endif + +#endif // _HEAVY_MESSAGE_H_ diff --git a/delay_simple/plugin/source/HvMessagePool.c b/delay_simple/plugin/source/HvMessagePool.c new file mode 100644 index 0000000..dbddb1d --- /dev/null +++ b/delay_simple/plugin/source/HvMessagePool.c @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvMessagePool.h" +#include "HvMessage.h" + +// the number of bytes reserved at a time from the pool +#define MP_BLOCK_SIZE_BYTES 512 + +#if HV_APPLE +#pragma mark - MessageList +#endif + +typedef struct MessageListNode { + char *p; + struct MessageListNode *next; +} MessageListNode; + +static inline bool ml_hasAvailable(HvMessagePoolList *ml) { + return (ml->head != NULL); +} + +static char *ml_pop(HvMessagePoolList *ml) { + MessageListNode *n = ml->head; + ml->head = n->next; + n->next = ml->pool; + ml->pool = n; + char *const p = n->p; + n->p = NULL; // set to NULL to make it clear that this node does not have a valid buffer + return p; +} + +/** Push a MessageListNode with the given pointer onto the head of the queue. */ +static void ml_push(HvMessagePoolList *ml, void *p) { + MessageListNode *n = NULL; + if (ml->pool != NULL) { + // take an empty MessageListNode from the pool + n = ml->pool; + ml->pool = n->next; + } else { + // a MessageListNode is not available, allocate one + n = (MessageListNode *) hv_malloc(sizeof(MessageListNode)); + hv_assert(n != NULL); + } + n->p = (char *) p; + n->next = ml->head; + ml->head = n; // push to the front of the queue +} + +static void ml_free(HvMessagePoolList *ml) { + if (ml != NULL) { + while (ml_hasAvailable(ml)) { + ml_pop(ml); + } + while (ml->pool != NULL) { + MessageListNode *n = ml->pool; + ml->pool = n->next; + hv_free(n); + } + } +} + +#if HV_APPLE +#pragma mark - HvMessagePool +#endif + +static hv_size_t mp_messagelistIndexForSize(hv_size_t byteSize) { + return (hv_size_t) hv_max_i((hv_min_max_log2((hv_uint32_t) byteSize) - 5), 0); +} + +hv_size_t mp_init(HvMessagePool *mp, hv_size_t numKB) { + mp->bufferSize = numKB * 1024; + mp->buffer = (char *) hv_malloc(mp->bufferSize); + hv_assert(mp->buffer != NULL); + mp->bufferIndex = 0; + + // initialise all message lists + for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { + mp->lists[i].head = NULL; + mp->lists[i].pool = NULL; + } + + return mp->bufferSize; +} + +void mp_free(HvMessagePool *mp) { + hv_free(mp->buffer); + for (int i = 0; i < MP_NUM_MESSAGE_LISTS; i++) { + ml_free(&mp->lists[i]); + } +} + +void mp_freeMessage(HvMessagePool *mp, HvMessage *m) { + const hv_size_t b = msg_getSize(m); // the number of bytes that a message occupies in memory + const hv_size_t i = mp_messagelistIndexForSize(b); // the HvMessagePoolList index in the pool + HvMessagePoolList *ml = &mp->lists[i]; + const hv_size_t chunkSize = 32 << i; + hv_memclear(m, chunkSize); // clear the chunk, just in case + ml_push(ml, m); +} + +HvMessage *mp_addMessage(HvMessagePool *mp, const HvMessage *m) { + const hv_size_t b = msg_getSize(m); + // determine the message list index to allocate data from based on the msg size + // smallest chunk size is 32 bytes + const hv_size_t i = mp_messagelistIndexForSize(b); + + hv_assert(i < MP_NUM_MESSAGE_LISTS); // how many chunk sizes do we want to support? 32, 64, 128, 256 at the moment + HvMessagePoolList *ml = &mp->lists[i]; + const hv_size_t chunkSize = 32 << i; + + if (ml_hasAvailable(ml)) { + char *buf = ml_pop(ml); + msg_copyToBuffer(m, buf, chunkSize); + return (HvMessage *) buf; + } else { + // if no appropriately sized buffer is immediately available, increase the size of the used buffer + const hv_size_t newIndex = mp->bufferIndex + MP_BLOCK_SIZE_BYTES; + hv_assert((newIndex <= mp->bufferSize) && + "The message pool buffer size has been exceeded. The context cannot store more messages. " + "Try using the new_with_options() initialiser with a larger pool size (default is 10KB)."); + + for (hv_size_t j = mp->bufferIndex; j < newIndex; j += chunkSize) { + ml_push(ml, mp->buffer + j); // push new nodes onto the list with chunk pointers + } + mp->bufferIndex = newIndex; + char *buf = ml_pop(ml); + msg_copyToBuffer(m, buf, chunkSize); + return (HvMessage *) buf; + } +} diff --git a/delay_simple/plugin/source/HvMessagePool.h b/delay_simple/plugin/source/HvMessagePool.h new file mode 100644 index 0000000..308318e --- /dev/null +++ b/delay_simple/plugin/source/HvMessagePool.h @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MESSAGE_POOL_H_ +#define _MESSAGE_POOL_H_ + +#include "HvUtils.h" + +#define MP_NUM_MESSAGE_LISTS 4 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct HvMessagePoolList { + struct MessageListNode *head; // list of currently available blocks + struct MessageListNode *pool; // list of currently used blocks +} HvMessagePoolList; + +typedef struct HvMessagePool { + char *buffer; // the buffer of all messages + hv_size_t bufferSize; // in bytes + hv_size_t bufferIndex; // the number of total reserved bytes + + HvMessagePoolList lists[MP_NUM_MESSAGE_LISTS]; +} HvMessagePool; + +/** + * The HvMessagePool is a basic memory management system. It reserves a large block of memory at initialisation + * and proceeds to divide this block into smaller chunks (usually 512 bytes) as they are needed. These chunks are + * further divided into 32, 64, 128, or 256 sections. Each of these sections is managed by a HvMessagePoolList (MPL). + * An MPL is a linked-list data structure which is initialised such that its own pool of listnodes is filled with nodes + * that point at each subblock (e.g. each 32-byte block of a 512-block chunk). + * + * HvMessagePool is loosely inspired by TCMalloc. http://goog-perftools.sourceforge.net/doc/tcmalloc.html + */ + +hv_size_t mp_init(struct HvMessagePool *mp, hv_size_t numKB); + +void mp_free(struct HvMessagePool *mp); + +/** + * Adds a message to the pool and returns a pointer to the copy. Returns NULL + * if no space was available in the pool. + */ +struct HvMessage *mp_addMessage(struct HvMessagePool *mp, const struct HvMessage *m); + +void mp_freeMessage(struct HvMessagePool *mp, struct HvMessage *m); + +#ifdef __cplusplus +} +#endif + +#endif // _MESSAGE_POOL_H_ diff --git a/delay_simple/plugin/source/HvMessageQueue.c b/delay_simple/plugin/source/HvMessageQueue.c new file mode 100644 index 0000000..2eeab8f --- /dev/null +++ b/delay_simple/plugin/source/HvMessageQueue.c @@ -0,0 +1,215 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvMessageQueue.h" + +hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB) { + hv_assert(poolSizeKB > 0); + q->head = NULL; + q->tail = NULL; + q->pool = NULL; + return mp_init(&q->mp, poolSizeKB); +} + +void mq_free(HvMessageQueue *q) { + mq_clear(q); + while (q->pool != NULL) { + MessageNode *n = q->pool; + q->pool = q->pool->next; + hv_free(n); + } + mp_free(&q->mp); +} + +static MessageNode *mq_getOrCreateNodeFromPool(HvMessageQueue *q) { + if (q->pool == NULL) { + // if necessary, create a new empty node + q->pool = (MessageNode *) hv_malloc(sizeof(MessageNode)); + hv_assert(q->pool != NULL); + q->pool->next = NULL; + } + MessageNode *node = q->pool; + q->pool = q->pool->next; + return node; +} + +int mq_size(HvMessageQueue *q) { + int size = 0; + MessageNode *n = q->head; + while (n != NULL) { + ++size; + n = n->next; + } + return size; +} + +HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + MessageNode *node = mq_getOrCreateNodeFromPool(q); + node->m = mp_addMessage(&q->mp, m); + node->let = let; + node->sendMessage = sendMessage; + node->prev = NULL; + node->next = NULL; + + if (q->tail != NULL) { + // the list already contains elements + q->tail->next = node; + node->prev = q->tail; + q->tail = node; + } else { + // the list is empty + node->prev = NULL; + q->head = node; + q->tail = node; + } + return mq_node_getMessage(node); +} + +HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (mq_hasMessage(q)) { + MessageNode *n = mq_getOrCreateNodeFromPool(q); + n->m = mp_addMessage(&q->mp, m); + n->let = let; + n->sendMessage = sendMessage; + + if (msg_getTimestamp(m) < msg_getTimestamp(q->head->m)) { + // the message occurs before the current head + n->next = q->head; + q->head->prev = n; + n->prev = NULL; + q->head = n; + } else if (msg_getTimestamp(m) >= msg_getTimestamp(q->tail->m)) { + // the message occurs after the current tail + n->next = NULL; + n->prev = q->tail; + q->tail->next = n; + q->tail = n; + } else { + // the message occurs somewhere between the head and tail + MessageNode *node = q->head; + while (node != NULL) { + if (msg_getTimestamp(m) < msg_getTimestamp(node->next->m)) { + MessageNode *r = node->next; + node->next = n; + n->next = r; + n->prev = node; + r->prev = n; + break; + } + node = node->next; + } + } + return n->m; + } else { + // add a message to the head + return mq_addMessage(q, m, let, sendMessage); + } +} + +void mq_pop(HvMessageQueue *q) { + if (mq_hasMessage(q)) { + MessageNode *n = q->head; + + mp_freeMessage(&q->mp, n->m); + n->m = NULL; + + n->let = 0; + n->sendMessage = NULL; + + q->head = n->next; + if (q->head == NULL) { + q->tail = NULL; + } else { + q->head->prev = NULL; + } + n->next = q->pool; + n->prev = NULL; + q->pool = n; + } +} + +bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (mq_hasMessage(q)) { + if (mq_node_getMessage(q->head) == m) { // msg in head node + // only remove the message if sendMessage is the same as the stored one, + // if the sendMessage argument is NULL, it is not checked and will remove any matching message pointer + if (sendMessage == NULL || q->head->sendMessage == sendMessage) { + mq_pop(q); + return true; + } + } else { + MessageNode *prevNode = q->head; + MessageNode *currNode = q->head->next; + while ((currNode != NULL) && (currNode->m != m)) { + prevNode = currNode; + currNode = currNode->next; + } + if (currNode != NULL) { + if (sendMessage == NULL || currNode->sendMessage == sendMessage) { + mp_freeMessage(&q->mp, m); + currNode->m = NULL; + currNode->let = 0; + currNode->sendMessage = NULL; + if (currNode == q->tail) { // msg in tail node + prevNode->next = NULL; + q->tail = prevNode; + } else { // msg in middle node + prevNode->next = currNode->next; + currNode->next->prev = prevNode; + } + currNode->next = (q->pool == NULL) ? NULL : q->pool; + currNode->prev = NULL; + q->pool = currNode; + return true; + } + } + } + } + return false; +} + +void mq_clear(HvMessageQueue *q) { + while (mq_hasMessage(q)) { + mq_pop(q); + } +} + +void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp) { + MessageNode *n = q->tail; + while (n != NULL && timestamp <= msg_getTimestamp(n->m)) { + // free the node's message + mp_freeMessage(&q->mp, n->m); + n->m = NULL; + n->let = 0; + n->sendMessage = NULL; + + // the tail points at the previous node + q->tail = n->prev; + + // put the node back in the pool + n->next = q->pool; + n->prev = NULL; + if (q->pool != NULL) q->pool->prev = n; + q->pool = n; + + // update the tail node + n = q->tail; + } + + if (q->tail == NULL) q->head = NULL; +} diff --git a/delay_simple/plugin/source/HvMessageQueue.h b/delay_simple/plugin/source/HvMessageQueue.h new file mode 100644 index 0000000..a35e4aa --- /dev/null +++ b/delay_simple/plugin/source/HvMessageQueue.h @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MESSAGE_QUEUE_H_ +#define _MESSAGE_QUEUE_H_ + +#include "HvMessage.h" +#include "HvMessagePool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +class HeavyContextInterface; +#else +typedef struct HeavyContextInterface HeavyContextInterface; +#endif + +typedef struct MessageNode { + struct MessageNode *prev; // doubly linked list + struct MessageNode *next; + HvMessage *m; + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *); + int let; +} MessageNode; + +/** A doubly linked list containing scheduled messages. */ +typedef struct HvMessageQueue { + MessageNode *head; // the head of the queue + MessageNode *tail; // the tail of the queue + MessageNode *pool; // the head of the reserve pool + HvMessagePool mp; +} HvMessageQueue; + +hv_size_t mq_initWithPoolSize(HvMessageQueue *q, hv_size_t poolSizeKB); + +void mq_free(HvMessageQueue *q); + +int mq_size(HvMessageQueue *q); + +static inline HvMessage *mq_node_getMessage(MessageNode *n) { + return n->m; +} + +static inline int mq_node_getLet(MessageNode *n) { + return n->let; +} + +static inline bool mq_hasMessage(HvMessageQueue *q) { + return (q->head != NULL); +} + +// true if there is a message and it occurs before (<) timestamp +static inline bool mq_hasMessageBefore(HvMessageQueue *const q, const hv_uint32_t timestamp) { + return mq_hasMessage(q) && (msg_getTimestamp(mq_node_getMessage(q->head)) < timestamp); +} + +static inline MessageNode *mq_peek(HvMessageQueue *q) { + return q->head; +} + +/** Appends the message to the end of the queue. */ +HvMessage *mq_addMessage(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Insert in ascending order the message acccording to its timestamp. */ +HvMessage *mq_addMessageByTimestamp(HvMessageQueue *q, const HvMessage *m, int let, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Pop the message at the head of the queue (and free its memory). */ +void mq_pop(HvMessageQueue *q); + +/** Remove a message from the queue (and free its memory) */ +bool mq_removeMessage(HvMessageQueue *q, HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +/** Clears (and frees) all messages in the queue. */ +void mq_clear(HvMessageQueue *q); + +/** Removes all messages occuring at or after the given timestamp. */ +void mq_clearAfter(HvMessageQueue *q, const hv_uint32_t timestamp); + +#ifdef __cplusplus +} +#endif + +#endif // _MESSAGE_QUEUE_H_ diff --git a/delay_simple/plugin/source/HvSignalTabread.c b/delay_simple/plugin/source/HvSignalTabread.c new file mode 100644 index 0000000..9197cf9 --- /dev/null +++ b/delay_simple/plugin/source/HvSignalTabread.c @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvSignalTabread.h" + +hv_size_t sTabread_init(SignalTabread *o, HvTable *table, bool forceAlignedLoads) { + o->table = table; + o->head = 0; + o->forceAlignedLoads = forceAlignedLoads; + return 0; +} + +void sTabread_onMessage(HeavyContextInterface *_c, SignalTabread *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + case 0: { + if (o->table != NULL) { + switch (msg_getType(m,0)) { + case HV_MSG_BANG: o->head = 0; break; + case HV_MSG_FLOAT: { + hv_uint32_t h = (hv_uint32_t) hv_abs_f(msg_getFloat(m,0)); + if (msg_getFloat(m,0) < 0.0f) { + // if input is negative, wrap around the end of the table + h = hTable_getSize(o->table) - h; + } + o->head = o->forceAlignedLoads ? (h & ~HV_N_SIMD_MASK) : h; + + // output new head + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), (float) o->head); + sendMessage(_c, 1, n); + break; + } + default: break; + } + } + break; + } + case 1: { + if (msg_isHashLike(m,0)) { + o->table = hv_table_get(_c, msg_getHash(m,0)); + } + break; + } + default: break; + } +} + + + +#if HV_APPLE +#pragma mark - Tabhead +#endif + +void sTabhead_onMessage(HeavyContextInterface *_c, SignalTabhead *o, const HvMessage *m) { + if (msg_isHashLike(m,0)) { + o->table = hv_table_get(_c, msg_getHash(m,0)); + } +} + +hv_size_t sTabhead_init(SignalTabhead *o, HvTable *table) { + o->table = table; + return 0; +} diff --git a/delay_simple/plugin/source/HvSignalTabread.h b/delay_simple/plugin/source/HvSignalTabread.h new file mode 100644 index 0000000..a3d330c --- /dev/null +++ b/delay_simple/plugin/source/HvSignalTabread.h @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_SIGNAL_TABREAD_H_ +#define _HEAVY_SIGNAL_TABREAD_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SignalTabread { + HvTable *table; // the table to read + hv_uint32_t head; + bool forceAlignedLoads; // false by default, true if using __hv_tabread_f +} SignalTabread; + +// random access to a table +hv_size_t sTabread_init(SignalTabread *o, HvTable *table, bool forceAlignedLoads); + + + +#if HV_APPLE +#pragma mark - Tabread - Random Access +#endif + +static inline void __hv_tabread_if(SignalTabread *o, hv_bIni_t bIn, hv_bOutf_t bOut) { + const float *const b = hTable_getBuffer(o->table); +#if HV_SIMD_AVX + const hv_int32_t *const i = (hv_int32_t *) &bIn; + + hv_assert(i[0] >= 0 && i[0] < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && i[1] < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && i[2] < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && i[3] < hTable_getAllocated(o->table)); + hv_assert(i[4] >= 0 && i[4] < hTable_getAllocated(o->table)); + hv_assert(i[5] >= 0 && i[5] < hTable_getAllocated(o->table)); + hv_assert(i[6] >= 0 && i[6] < hTable_getAllocated(o->table)); + hv_assert(i[7] >= 0 && i[7] < hTable_getAllocated(o->table)); + + *bOut = _mm256_set_ps(b[i[7]], b[i[6]], b[i[5]], b[i[4]], b[i[3]], b[i[2]], b[i[1]], b[i[0]]); +#elif HV_SIMD_SSE + const hv_int32_t *const i = (hv_int32_t *) &bIn; + + hv_assert(i[0] >= 0 && ((hv_uint32_t) i[0]) < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && ((hv_uint32_t) i[1]) < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && ((hv_uint32_t) i[2]) < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && ((hv_uint32_t) i[3]) < hTable_getAllocated(o->table)); + + *bOut = _mm_set_ps(b[i[3]], b[i[2]], b[i[1]], b[i[0]]); +#elif HV_SIMD_NEON + hv_assert((bIn[0] >= 0) && (bIn[0] < hTable_getAllocated(o->table))); + hv_assert((bIn[1] >= 0) && (bIn[1] < hTable_getAllocated(o->table))); + hv_assert((bIn[2] >= 0) && (bIn[2] < hTable_getAllocated(o->table))); + hv_assert((bIn[3] >= 0) && (bIn[3] < hTable_getAllocated(o->table))); + + *bOut = (float32x4_t) {b[bIn[0]], b[bIn[1]], b[bIn[2]], b[bIn[3]]}; +#else // HV_SIMD_NONE + hv_assert(bIn >= 0 && ((hv_uint32_t) bIn < hTable_getAllocated(o->table))); + + *bOut = b[bIn]; +#endif +} + + + +#if HV_APPLE +#pragma mark - Tabread - Linear Access +#endif + +// this tabread never stops reading. It is mainly intended for linear reads that loop around a table. +static inline void __hv_tabread_f(SignalTabread *o, hv_bOutf_t bOut) { + hv_assert((o->head + HV_N_SIMD) <= hTable_getAllocated(o->table)); // assert that we always read within the table bounds + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + *bOut = _mm256_load_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_SSE + *bOut = _mm_load_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_NEON + *bOut = vld1q_f32(hTable_getBuffer(o->table) + head); +#else // HV_SIMD_NONE + *bOut = *(hTable_getBuffer(o->table) + head); +#endif + o->head = head + HV_N_SIMD; +} + +// unaligned linear tabread, as above +static inline void __hv_tabreadu_f(SignalTabread *o, hv_bOutf_t bOut) { + hv_assert((o->head + HV_N_SIMD) <= hTable_getAllocated(o->table)); // assert that we always read within the table bounds + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + *bOut = _mm256_loadu_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_SSE + *bOut = _mm_loadu_ps(hTable_getBuffer(o->table) + head); +#elif HV_SIMD_NEON + *bOut = vld1q_f32(hTable_getBuffer(o->table) + head); +#else // HV_SIMD_NONE + *bOut = *(hTable_getBuffer(o->table) + head); +#endif + o->head = head + HV_N_SIMD; +} + +// this tabread can be instructed to stop. It is mainly intended for linear reads that only process a portion of a buffer. +static inline void __hv_tabread_stoppable_f(SignalTabread *o, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + if (o->head == ~0x0) { + *bOut = _mm256_setzero_ps(); + } else { + *bOut = _mm256_load_ps(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#elif HV_SIMD_SSE + if (o->head == ~0x0) { + *bOut = _mm_setzero_ps(); + } else { + *bOut = _mm_load_ps(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#elif HV_SIMD_NEON + if (o->head == ~0x0) { + *bOut = vdupq_n_f32(0.0f); + } else { + *bOut = vld1q_f32(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#else // HV_SIMD_NONE + if (o->head == ~0x0) { + *bOut = 0.0f; + } else { + *bOut = *(hTable_getBuffer(o->table) + o->head); + o->head += HV_N_SIMD; + } +#endif +} + +void sTabread_onMessage(HeavyContextInterface *_c, SignalTabread *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + + + +#if HV_APPLE +#pragma mark - Tabhead +#endif + +typedef struct SignalTabhead { + HvTable *table; +} SignalTabhead; + +hv_size_t sTabhead_init(SignalTabhead *o, HvTable *table); + +static inline void __hv_tabhead_f(SignalTabhead *o, hv_bOutf_t bOut) { +#if HV_SIMD_AVX + *bOut = _mm256_set1_ps((float) hTable_getHead(o->table)); +#elif HV_SIMD_SSE + *bOut = _mm_set1_ps((float) hTable_getHead(o->table)); +#elif HV_SIMD_NEON + *bOut = vdupq_n_f32((float32_t) hTable_getHead(o->table)); +#else // HV_SIMD_NONE + *bOut = (float) hTable_getHead(o->table); +#endif +} + +void sTabhead_onMessage(HeavyContextInterface *_c, SignalTabhead *o, const HvMessage *m); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_SIGNAL_TABREAD_H_ diff --git a/delay_simple/plugin/source/HvSignalTabwrite.c b/delay_simple/plugin/source/HvSignalTabwrite.c new file mode 100644 index 0000000..f3d984a --- /dev/null +++ b/delay_simple/plugin/source/HvSignalTabwrite.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvSignalTabwrite.h" + +hv_size_t sTabwrite_init(SignalTabwrite *o, HvTable *table) { + o->table = table; + o->head = 0; + return 0; +} + +void sTabwrite_onMessage(HeavyContextInterface *_c, SignalTabwrite *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + switch (letIn) { + // inlet 0 is the signal inlet + case 1: { + switch (msg_getType(m,0)) { + case HV_MSG_BANG: o->head = 0; break; + case HV_MSG_FLOAT: { + o->head = (msg_getFloat(m,0) >= 0.0f) ? (hv_uint32_t) msg_getFloat(m,0) : HV_TABWRITE_STOPPED; + break; + } + case HV_MSG_SYMBOL: { + if (msg_compareSymbol(m, 0, "stop")) { + o->head = HV_TABWRITE_STOPPED; + } + break; + } + default: break; + } + break; + } + case 2: { + if (msg_isHashLike(m,0)) { + o->table = hv_table_get(_c, msg_getHash(m,0)); + } + break; + } + default: break; + } +} diff --git a/delay_simple/plugin/source/HvSignalTabwrite.h b/delay_simple/plugin/source/HvSignalTabwrite.h new file mode 100644 index 0000000..cadf1e0 --- /dev/null +++ b/delay_simple/plugin/source/HvSignalTabwrite.h @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_SIGNAL_TABWRITE_H_ +#define _HEAVY_SIGNAL_TABWRITE_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HV_TABWRITE_STOPPED -1 // ~0x0 + +typedef struct SignalTabwrite { + HvTable *table; + hv_uint32_t head; // local write head. Where this object has most recently written to the table. +} SignalTabwrite; + +hv_size_t sTabwrite_init(SignalTabwrite *o, HvTable *table); + +void sTabwrite_onMessage(HeavyContextInterface *_c, SignalTabwrite *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +// linear write to table +static inline void __hv_tabwrite_f(SignalTabwrite *o, hv_bInf_t bIn) { + hv_assert((o->head + HV_N_SIMD) <= hTable_getSize(o->table)); // assert that the table bounds are respected + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + _mm256_store_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_SSE + _mm_store_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_NEON + vst1q_f32(hTable_getBuffer(o->table) + head, bIn); +#else // HV_SIMD_NONE + *(hTable_getBuffer(o->table) + head) = bIn; +#endif + head += HV_N_SIMD; + o->head = head; // update local write head + hTable_setHead(o->table, head); // update the remote write head (e.g. for use by vd~) +} + +// linear unaligned write to table +static inline void __hv_tabwriteu_f(SignalTabwrite *o, hv_bInf_t bIn) { + hv_uint32_t head = o->head; +#if HV_SIMD_AVX + _mm256_storeu_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_SSE + _mm_storeu_ps(hTable_getBuffer(o->table) + head, bIn); +#elif HV_SIMD_NEON + vst1q_f32(hTable_getBuffer(o->table) + head, bIn); +#else // HV_SIMD_NONE + *(hTable_getBuffer(o->table) + head) = bIn; +#endif + head += HV_N_SIMD; + o->head = head; // update local write head + hTable_setHead(o->table, head); // update remote write head +} + +// this tabread can be instructed to stop. It is mainly intended for linear reads that only process a portion of a buffer. +// Stores are unaligned, which can be slow but allows any indicies to be written to. +// TODO(mhroth): this is not stopping! +static inline void __hv_tabwrite_stoppable_f(SignalTabwrite *o, hv_bInf_t bIn) { + if (o->head != HV_TABWRITE_STOPPED) { +#if HV_SIMD_AVX + _mm256_storeu_ps(hTable_getBuffer(o->table) + o->head, bIn); +#elif HV_SIMD_SSE + _mm_storeu_ps(hTable_getBuffer(o->table) + o->head, bIn); +#elif HV_SIMD_NEON + vst1q_f32(hTable_getBuffer(o->table) + o->head, bIn); +#else // HV_SIMD_NONE + *(hTable_getBuffer(o->table) + o->head) = bIn; +#endif + o->head += HV_N_SIMD; + } +} + +// random write to table +static inline void __hv_tabwrite_if(SignalTabwrite *o, hv_bIni_t bIn0, hv_bInf_t bIn1) { + float *const b = hTable_getBuffer(o->table); +#if HV_SIMD_AVX + const hv_int32_t *const i = (hv_int32_t *) &bIn0; + const float *const f = (float *) &bIn1; + + hv_assert(i[0] >= 0 && i[0] < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && i[1] < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && i[2] < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && i[3] < hTable_getAllocated(o->table)); + hv_assert(i[4] >= 0 && i[4] < hTable_getAllocated(o->table)); + hv_assert(i[5] >= 0 && i[5] < hTable_getAllocated(o->table)); + hv_assert(i[6] >= 0 && i[6] < hTable_getAllocated(o->table)); + hv_assert(i[7] >= 0 && i[7] < hTable_getAllocated(o->table)); + + b[i[0]] = f[0]; + b[i[1]] = f[1]; + b[i[2]] = f[2]; + b[i[3]] = f[3]; + b[i[4]] = f[4]; + b[i[5]] = f[5]; + b[i[6]] = f[6]; + b[i[7]] = f[7]; +#elif HV_SIMD_SSE + const hv_int32_t *const i = (hv_int32_t *) &bIn0; + const float *const f = (float *) &bIn1; + + hv_assert(i[0] >= 0 && ((hv_uint32_t) i[0]) < hTable_getAllocated(o->table)); + hv_assert(i[1] >= 0 && ((hv_uint32_t) i[1]) < hTable_getAllocated(o->table)); + hv_assert(i[2] >= 0 && ((hv_uint32_t) i[2]) < hTable_getAllocated(o->table)); + hv_assert(i[3] >= 0 && ((hv_uint32_t) i[3]) < hTable_getAllocated(o->table)); + + b[i[0]] = f[0]; + b[i[1]] = f[1]; + b[i[2]] = f[2]; + b[i[3]] = f[3]; +#elif HV_SIMD_NEON + hv_assert((vgetq_lane_s32(bIn0,0) >= 0) && (vgetq_lane_s32(bIn0,0) < hTable_getSize(o->table))); + hv_assert((vgetq_lane_s32(bIn0,1) >= 0) && (vgetq_lane_s32(bIn0,1) < hTable_getSize(o->table))); + hv_assert((vgetq_lane_s32(bIn0,2) >= 0) && (vgetq_lane_s32(bIn0,2) < hTable_getSize(o->table))); + hv_assert((vgetq_lane_s32(bIn0,3) >= 0) && (vgetq_lane_s32(bIn0,3) < hTable_getSize(o->table))); + + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 0), bIn1, 0); + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 1), bIn1, 1); + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 2), bIn1, 2); + vst1q_lane_f32(b + vgetq_lane_s32(bIn0, 3), bIn1, 3); +#else // HV_SIMD_NONE + b[bIn0] = bIn1; +#endif +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_SIGNAL_TABWRITE_H_ diff --git a/delay_simple/plugin/source/HvSignalVar.c b/delay_simple/plugin/source/HvSignalVar.c new file mode 100644 index 0000000..393cd23 --- /dev/null +++ b/delay_simple/plugin/source/HvSignalVar.c @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvSignalVar.h" + +// __var~f + +static void sVarf_update(SignalVarf *o, float k, float step, bool reverse) { +#if HV_SIMD_AVX + if (reverse) o->v = _mm256_setr_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k); + else o->v = _mm256_set_ps(k+7.0f*step, k+6.0f*step, k+5.0f*step, k+4.0f*step, k+3.0f*step, k+2.0f*step, k+step, k); +#elif HV_SIMD_SSE + if (reverse) o->v = _mm_setr_ps(k+3.0f*step, k+2.0f*step, k+step, k); + else o->v = _mm_set_ps(k+3.0f*step, k+2.0f*step, k+step, k); +#elif HV_SIMD_NEON + if (reverse) o->v = (float32x4_t) {3.0f*step+k, 2.0f*step+k, step+k, k}; + else o->v = (float32x4_t) {k, step+k, 2.0f*step+k, 3.0f*step+k}; +#else // HV_SIMD_NONE + o->v = k; +#endif +} + +hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse) { + sVarf_update(o, k, step, reverse); + return 0; +} + +void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m) { + if (msg_isFloat(m,0)) { + sVarf_update(o, msg_getFloat(m,0), msg_isFloat(m,1) ? msg_getFloat(m,1) : 0.0f, msg_getNumElements(m) == 3); + } +} + + + +// __var~i + +static void sVari_update(SignalVari *o, int k, int step, bool reverse) { +#if HV_SIMD_AVX + if (reverse) o->v = _mm256_setr_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k); + else o->v = _mm256_set_epi32(k+7*step, k+6*step, k+5*step, k+4*step, k+3*step, k+2*step, k+step, k); +#elif HV_SIMD_SSE + if (reverse) o->v = _mm_setr_epi32(k+3*step, k+2*step, k+step, k); + else o->v = _mm_set_epi32(k+3*step, k+2*step, k+step, k); +#elif HV_SIMD_NEON + if (reverse) o->v = (int32x4_t) {3*step+k, 2*step+k, step+k, k}; + else o->v = (int32x4_t) {k, step+k, 2*step+k, 3*step+k}; +#else // HV_SIMD_NEON + o->v = k; +#endif +} + +hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse) { + sVari_update(o, k, step, reverse); + return 0; +} + +void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m) { + if (msg_isFloat(m,0)) { + sVari_update(o, (int) msg_getFloat(m,0), msg_isFloat(m,1) ? (int) msg_getFloat(m,1) : 0, msg_getNumElements(m) == 3); + } +} diff --git a/delay_simple/plugin/source/HvSignalVar.h b/delay_simple/plugin/source/HvSignalVar.h new file mode 100644 index 0000000..6502f6b --- /dev/null +++ b/delay_simple/plugin/source/HvSignalVar.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_SIGNAL_VAR_H_ +#define _HEAVY_SIGNAL_VAR_H_ + +#include "HvHeavyInternal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// __var~f, __varread~f, __varwrite~f + +typedef struct SignalVarf { + hv_bufferf_t v; +} SignalVarf; + +hv_size_t sVarf_init(SignalVarf *o, float k, float step, bool reverse); + +static inline void __hv_varread_f(SignalVarf *o, hv_bOutf_t bOut) { + *bOut = o->v; +} + +static inline void __hv_varwrite_f(SignalVarf *o, hv_bInf_t bIn) { + o->v = bIn; +} + +void sVarf_onMessage(HeavyContextInterface *_c, SignalVarf *o, const HvMessage *m); + + + +// __var~i, __varread~i, __varwrite~i + +typedef struct SignalVari { + hv_bufferi_t v; +} SignalVari; + +hv_size_t sVari_init(SignalVari *o, int k, int step, bool reverse); + +static inline void __hv_varread_i(SignalVari *o, hv_bOuti_t bOut) { + *bOut = o->v; +} + +static inline void __hv_varwrite_i(SignalVari *o, hv_bIni_t bIn) { + o->v = bIn; +} + +void sVari_onMessage(HeavyContextInterface *_c, SignalVari *o, const HvMessage *m); + + + +// __var_k~f, __var_k~i + +#if HV_SIMD_AVX +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_h,_g,_f,_e,_d,_c,_b,_a) +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_epi32(_a,_b,_c,_d,_e,_f,_g,_h) +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_h,_g,_f,_e,_d,_c,_b,_a) +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm256_set_ps(_a,_b,_c,_d,_e,_f,_g,_h) +#elif HV_SIMD_SSE +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_d,_c,_b,_a) +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_epi32(_a,_b,_c,_d) +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_d,_c,_b,_a) +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_mm_set_ps(_a,_b,_c,_d) +#elif HV_SIMD_NEON +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_a,_b,_c,_d}) +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((int32x4_t) {_d,_c,_b,_a}) +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_a,_b,_c,_d}) +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=((float32x4_t) {_d,_c,_b,_a}) +#else // HV_SIMD_NONE +#define __hv_var_k_i(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#define __hv_var_k_i_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#define __hv_var_k_f(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#define __hv_var_k_f_r(_z,_a,_b,_c,_d,_e,_f,_g,_h) *_z=_a +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_SIGNAL_VAR_H_ diff --git a/delay_simple/plugin/source/HvTable.c b/delay_simple/plugin/source/HvTable.c new file mode 100644 index 0000000..1b2cd38 --- /dev/null +++ b/delay_simple/plugin/source/HvTable.c @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvTable.h" +#include "HvMessage.h" + +hv_size_t hTable_init(HvTable *o, int length) { + o->length = length; + // true size of the table is always an integer multple of HV_N_SIMD + o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; + // add an extra length for mirroring + o->allocated = o->size + HV_N_SIMD; + o->head = 0; + hv_size_t numBytes = o->allocated * sizeof(float); + o->buffer = (float *) hv_malloc(numBytes); + hv_assert(o->buffer != NULL); + hv_memclear(o->buffer, numBytes); + return numBytes; +} + +hv_size_t hTable_initWithData(HvTable *o, int length, const float *data) { + o->length = length; + o->size = (length + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; + o->allocated = o->size + HV_N_SIMD; + o->head = 0; + hv_size_t numBytes = o->size * sizeof(float); + o->buffer = (float *) hv_malloc(numBytes); + hv_assert(o->buffer != NULL); + hv_memclear(o->buffer, numBytes); + hv_memcpy(o->buffer, data, length*sizeof(float)); + return numBytes; +} + +hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data) { + o->length = length; + o->size = length; + o->allocated = length; + o->buffer = data; + o->head = 0; + return 0; +} + +void hTable_free(HvTable *o) { + hv_free(o->buffer); +} + +int hTable_resize(HvTable *o, hv_uint32_t newLength) { + // TODO(mhroth): update context with memory allocated by table + // NOTE(mhroth): mirrored bytes are not necessarily carried over + const hv_uint32_t newSize = (newLength + HV_N_SIMD_MASK) & ~HV_N_SIMD_MASK; + if (newSize == o->size) return 0; // early exit if no change in size + const hv_uint32_t oldSizeBytes = (hv_uint32_t) (o->size * sizeof(float)); + const hv_uint32_t newAllocated = newSize + HV_N_SIMD; + const hv_uint32_t newAllocatedBytes = (hv_uint32_t) (newAllocated * sizeof(float)); + + float *b = (float *) hv_realloc(o->buffer, newAllocatedBytes); + hv_assert(b != NULL); // error while reallocing! + // ensure that hv_realloc has given us a correctly aligned buffer + if ((((hv_uintptr_t) (const void *) b) & ((0x1< o->size) { + hv_memclear(b + o->size, (newAllocated - o->size) * sizeof(float)); // clear new parts of the buffer + } + o->buffer = b; + } else { + // if not, we have to re-malloc ourselves + char *c = (char *) hv_malloc(newAllocatedBytes); + hv_assert(c != NULL); // error while allocating new buffer! + if (newAllocatedBytes > oldSizeBytes) { + hv_memcpy(c, b, oldSizeBytes); + hv_memclear(c + oldSizeBytes, newAllocatedBytes - oldSizeBytes); + } else { + hv_memcpy(c, b, newAllocatedBytes); + } + hv_free(b); + o->buffer = (float *) c; + } + o->length = newLength; + o->size = newSize; + o->allocated = newAllocated; + return (int) (newAllocated - oldSizeBytes - (HV_N_SIMD*sizeof(float))); +} + +void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)) { + if (msg_compareSymbol(m,0,"resize") && msg_isFloat(m,1) && msg_getFloat(m,1) >= 0.0f) { + hTable_resize(o, (int) hv_ceil_f(msg_getFloat(m,1))); // apply ceil to ensure that tables always have enough space + + // send out the new size of the table + HvMessage *n = HV_MESSAGE_ON_STACK(1); + msg_initWithFloat(n, msg_getTimestamp(m), (float) hTable_getSize(o)); + sendMessage(_c, 0, n); + } + + else if (msg_compareSymbol(m,0,"mirror")) { + hv_memcpy(o->buffer+o->size, o->buffer, HV_N_SIMD*sizeof(float)); + } +} diff --git a/delay_simple/plugin/source/HvTable.h b/delay_simple/plugin/source/HvTable.h new file mode 100644 index 0000000..1d74887 --- /dev/null +++ b/delay_simple/plugin/source/HvTable.h @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_TABLE_H_ +#define _HEAVY_TABLE_H_ + +#include "HvHeavy.h" +#include "HvUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct HvTable { + float *buffer; + // the number of values that the table is requested to have + hv_uint32_t length; + + // the number of usable values that the table actually has + // this is always an even multiple of HV_N_SIMD + hv_uint32_t size; + + // Note that the true size of the table is (size + HV_N_SIMD), + // with the trailing values used by the system, e.g. to create a circular + // buffer + hv_uint32_t allocated; + + hv_uint32_t head; // the most recently written point +} HvTable; + +hv_size_t hTable_init(HvTable *o, int length); + +hv_size_t hTable_initWithData(HvTable *o, int length, const float *data); + +hv_size_t hTable_initWithFinalData(HvTable *o, int length, float *data); + +void hTable_free(HvTable *o); + +int hTable_resize(HvTable *o, hv_uint32_t newLength); + +void hTable_onMessage(HeavyContextInterface *_c, HvTable *o, int letIn, const HvMessage *m, + void (*sendMessage)(HeavyContextInterface *, int, const HvMessage *)); + +static inline float *hTable_getBuffer(HvTable *o) { + return o->buffer; +} + +// the user-requested length of the table (number of floats) +static inline hv_uint32_t hTable_getLength(HvTable *o) { + return o->length; +} + +// the usable length of the table (an even multiple of HV_N_SIMD) +static inline hv_uint32_t hTable_getSize(HvTable *o) { + return o->size; +} + +// the number of floats allocated to this table (usually size + HV_N_SIMD) +static inline hv_uint32_t hTable_getAllocated(HvTable *o) { + return o->allocated; +} + +static inline hv_uint32_t hTable_getHead(HvTable *o) { + return o->head; +} + +static inline void hTable_setHead(HvTable *o, hv_uint32_t head) { + o->head = head; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _HEAVY_TABLE_H_ diff --git a/delay_simple/plugin/source/HvUtils.c b/delay_simple/plugin/source/HvUtils.c new file mode 100644 index 0000000..2fdf682 --- /dev/null +++ b/delay_simple/plugin/source/HvUtils.c @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "HvUtils.h" + +hv_uint32_t hv_string_to_hash(const char *str) { + // this hash is based MurmurHash2 + // http://en.wikipedia.org/wiki/MurmurHash + // https://sites.google.com/site/murmurhash/ + static const hv_uint32_t n = 0x5bd1e995; + static const hv_int32_t r = 24; + + if (str == NULL) return 0; + + hv_uint32_t len = (hv_uint32_t) hv_strlen(str); + hv_uint32_t x = len; // seed (0) ^ len + + while (len >= 4) { +#if HV_EMSCRIPTEN + hv_uint32_t k = str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24); +#else + hv_uint32_t k = *((hv_uint32_t *) str); +#endif + k *= n; + k ^= (k >> r); + k *= n; + x *= n; + x ^= k; + str += 4; len -= 4; + } + switch (len) { + case 3: x ^= (str[2] << 16); + case 2: x ^= (str[1] << 8); + case 1: x ^= str[0]; x *= n; + default: break; + } + x ^= (x >> 13); + x *= n; + x ^= (x >> 15); + return x; +} diff --git a/delay_simple/plugin/source/HvUtils.h b/delay_simple/plugin/source/HvUtils.h new file mode 100644 index 0000000..3be2432 --- /dev/null +++ b/delay_simple/plugin/source/HvUtils.h @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2014-2018 Enzien Audio Ltd. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HEAVY_UTILS_H_ +#define _HEAVY_UTILS_H_ + +// platform definitions +#if _WIN32 || _WIN64 || _MSC_VER + #define HV_WIN 1 +#elif __APPLE__ + #define HV_APPLE 1 +#elif __ANDROID__ + #define HV_ANDROID 1 +#elif __unix__ || __unix + #define HV_UNIX 1 +#else + #warning Could not detect platform. Assuming Unix-like. +#endif + +#ifdef EMSCRIPTEN +#define HV_EMSCRIPTEN 1 +#endif + +// basic includes +#include +#include +#include + +// type definitions +#include +#include +#define hv_uint8_t uint8_t +#define hv_int16_t int16_t +#define hv_uint16_t uint16_t +#define hv_int32_t int32_t +#define hv_uint32_t uint32_t +#define hv_uint64_t uint64_t +#define hv_size_t size_t +#define hv_uintptr_t uintptr_t + +// SIMD-specific includes +#if !(HV_SIMD_NONE || HV_SIMD_NEON || HV_SIMD_SSE || HV_SIMD_AVX) + #define HV_SIMD_NEON __ARM_NEON__ + #define HV_SIMD_SSE (__SSE__ && __SSE2__ && __SSE3__ && __SSSE3__ && __SSE4_1__) + #define HV_SIMD_AVX (__AVX__ && HV_SIMD_SSE) +#endif +#ifndef HV_SIMD_FMA + #define HV_SIMD_FMA __FMA__ +#endif + +#if HV_SIMD_AVX || HV_SIMD_SSE + #include +#elif HV_SIMD_NEON + #include +#endif + +#if HV_SIMD_NEON // NEON + #define HV_N_SIMD 4 + #define hv_bufferf_t float32x4_t + #define hv_bufferi_t int32x4_t + #define hv_bInf_t float32x4_t + #define hv_bOutf_t float32x4_t* + #define hv_bIni_t int32x4_t + #define hv_bOuti_t int32x4_t* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#elif HV_SIMD_AVX // AVX + #define HV_N_SIMD 8 + #define hv_bufferf_t __m256 + #define hv_bufferi_t __m256i + #define hv_bInf_t __m256 + #define hv_bOutf_t __m256* + #define hv_bIni_t __m256i + #define hv_bOuti_t __m256i* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#elif HV_SIMD_SSE // SSE + #define HV_N_SIMD 4 + #define hv_bufferf_t __m128 + #define hv_bufferi_t __m128i + #define hv_bInf_t __m128 + #define hv_bOutf_t __m128* + #define hv_bIni_t __m128i + #define hv_bOuti_t __m128i* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#else // DEFAULT + #define HV_N_SIMD 1 + #undef HV_SIMD_NONE + #define HV_SIMD_NONE 1 + #define hv_bufferf_t float + #define hv_bufferi_t int + #define hv_bInf_t float + #define hv_bOutf_t float* + #define hv_bIni_t int + #define hv_bOuti_t int* + #define VIf(_x) (_x) + #define VOf(_x) (&_x) + #define VIi(_x) (_x) + #define VOi(_x) (&_x) +#endif + +#define HV_N_SIMD_MASK (HV_N_SIMD-1) + +// Strings +#include +#define hv_strlen(a) strlen(a) +#define hv_strcmp(a, b) strcmp(a, b) +#define hv_snprintf(a, b, c, ...) snprintf(a, b, c, __VA_ARGS__) +#if HV_WIN +#define hv_strncpy(_dst, _src, _len) strncpy_s(_dst, _len, _src, _TRUNCATE) +#else +#define hv_strncpy(_dst, _src, _len) strncpy(_dst, _src, _len) +#endif + +// Memory management +#define hv_memcpy(a, b, c) memcpy(a, b, c) +#define hv_memclear(a, b) memset(a, 0, b) +#if HV_WIN + #include + #define hv_alloca(_n) _alloca(_n) + #if HV_SIMD_AVX + #define hv_malloc(_n) _aligned_malloc(_n, 32) + #define hv_realloc(a, b) _aligned_realloc(a, b, 32) + #define hv_free(x) _aligned_free(x) + #elif HV_SIMD_SSE || HV_SIMD_NEON + #define hv_malloc(_n) _aligned_malloc(_n, 16) + #define hv_realloc(a, b) _aligned_realloc(a, b, 16) + #define hv_free(x) _aligned_free(x) + #else // HV_SIMD_NONE + #define hv_malloc(_n) malloc(_n) + #define hv_realloc(a, b) realloc(a, b) + #define hv_free(_n) free(_n) + #endif +#elif HV_APPLE + #define hv_alloca(_n) alloca(_n) + #define hv_realloc(a, b) realloc(a, b) + #if HV_SIMD_AVX + #include + #define hv_malloc(_n) _mm_malloc(_n, 32) + #define hv_free(x) _mm_free(x) + #elif HV_SIMD_SSE + #include + #define hv_malloc(_n) _mm_malloc(_n, 16) + #define hv_free(x) _mm_free(x) + #elif HV_SIMD_NEON + // malloc on ios always has 16-byte alignment + #define hv_malloc(_n) malloc(_n) + #define hv_free(x) free(x) + #else // HV_SIMD_NONE + #define hv_malloc(_n) malloc(_n) + #define hv_free(x) free(x) + #endif +#else + #include + #define hv_alloca(_n) alloca(_n) + #define hv_realloc(a, b) realloc(a, b) + #if HV_SIMD_AVX + #define hv_malloc(_n) aligned_alloc(32, _n) + #define hv_free(x) free(x) + #elif HV_SIMD_SSE + #define hv_malloc(_n) aligned_alloc(16, _n) + #define hv_free(x) free(x) + #elif HV_SIMD_NEON + #if HV_ANDROID + #define hv_malloc(_n) memalign(16, _n) + #define hv_free(x) free(x) + #else + #define hv_malloc(_n) aligned_alloc(16, _n) + #define hv_free(x) free(x) + #endif + #else // HV_SIMD_NONE + #define hv_malloc(_n) malloc(_n) + #define hv_free(_n) free(_n) + #endif +#endif + +// Assert +#include +#define hv_assert(e) assert(e) + +// Export and Inline +#if HV_WIN +#define HV_EXPORT __declspec(dllexport) +#define inline __inline +#define HV_FORCE_INLINE __forceinline +#else +#define HV_EXPORT +#define HV_FORCE_INLINE inline __attribute__((always_inline)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + // Returns a 32-bit hash of any string. Returns 0 if string is NULL. + hv_uint32_t hv_string_to_hash(const char *str); +#ifdef __cplusplus +} +#endif + +// Math +#include +static inline hv_size_t __hv_utils_max_ui(hv_size_t x, hv_size_t y) { return (x > y) ? x : y; } +static inline hv_size_t __hv_utils_min_ui(hv_size_t x, hv_size_t y) { return (x < y) ? x : y; } +static inline hv_int32_t __hv_utils_max_i(hv_int32_t x, hv_int32_t y) { return (x > y) ? x : y; } +static inline hv_int32_t __hv_utils_min_i(hv_int32_t x, hv_int32_t y) { return (x < y) ? x : y; } +#define hv_max_ui(a, b) __hv_utils_max_ui(a, b) +#define hv_min_ui(a, b) __hv_utils_min_ui(a, b) +#define hv_max_i(a, b) __hv_utils_max_i(a, b) +#define hv_min_i(a, b) __hv_utils_min_i(a, b) +#define hv_max_f(a, b) fmaxf(a, b) +#define hv_min_f(a, b) fminf(a, b) +#define hv_max_d(a, b) fmax(a, b) +#define hv_min_d(a, b) fmin(a, b) +#define hv_sin_f(a) sinf(a) +#define hv_sinh_f(a) sinhf(a) +#define hv_cos_f(a) cosf(a) +#define hv_cosh_f(a) coshf(a) +#define hv_tan_f(a) tanf(a) +#define hv_tanh_f(a) tanhf(a) +#define hv_asin_f(a) asinf(a) +#define hv_asinh_f(a) asinhf(a) +#define hv_acos_f(a) acosf(a) +#define hv_acosh_f(a) acoshf(a) +#define hv_atan_f(a) atanf(a) +#define hv_atanh_f(a) atanhf(a) +#define hv_atan2_f(a, b) atan2f(a, b) +#define hv_exp_f(a) expf(a) +#define hv_abs_f(a) fabsf(a) +#define hv_sqrt_f(a) sqrtf(a) +#define hv_log_f(a) logf(a) +#define hv_ceil_f(a) ceilf(a) +#define hv_floor_f(a) floorf(a) +#define hv_round_f(a) roundf(a) +#define hv_pow_f(a, b) powf(a, b) +#if HV_EMSCRIPTEN +#define hv_fma_f(a, b, c) ((a*b)+c) // emscripten does not support fmaf (yet?) +#else +#define hv_fma_f(a, b, c) fmaf(a, b, c) +#endif +#if HV_WIN + // finds ceil(log2(x)) + #include + static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { + unsigned long z = 0; + _BitScanReverse(&z, x); + return (hv_uint32_t) (z+1); + } +#else + static inline hv_uint32_t __hv_utils_min_max_log2(hv_uint32_t x) { + return (hv_uint32_t) (32 - __builtin_clz(x-1)); + } +#endif +#define hv_min_max_log2(a) __hv_utils_min_max_log2(a) + +// Atomics +#if HV_WIN + #include + #define hv_atomic_bool volatile LONG + #define HV_SPINLOCK_ACQUIRE(_x) while (InterlockedCompareExchange(&_x, true, false)) { } + #define HV_SPINLOCK_TRY(_x) return !InterlockedCompareExchange(&_x, true, false) + #define HV_SPINLOCK_RELEASE(_x) (_x = false) +#elif HV_ANDROID + // Android support for atomics isn't that great, we'll do it manually + // https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html + #define hv_atomic_bool hv_uint8_t + #define HV_SPINLOCK_ACQUIRE(_x) while (__sync_lock_test_and_set(&_x, 1)) + #define HV_SPINLOCK_TRY(_x) return !__sync_lock_test_and_set(&_x, 1) + #define HV_SPINLOCK_RELEASE(_x) __sync_lock_release(&_x) +#elif __cplusplus + #include + #define hv_atomic_bool std::atomic_flag + #define HV_SPINLOCK_ACQUIRE(_x) while (_x.test_and_set(std::memory_order_acquire)) + #define HV_SPINLOCK_TRY(_x) return !_x.test_and_set(std::memory_order_acquire) + #define HV_SPINLOCK_RELEASE(_x) _x.clear(std::memory_order_release) +#elif defined(__has_include) + #if __has_include() + #include + #define hv_atomic_bool atomic_flag + #define HV_SPINLOCK_ACQUIRE(_x) while (atomic_flag_test_and_set_explicit(&_x, memory_order_acquire)) + #define HV_SPINLOCK_TRY(_x) return !atomic_flag_test_and_set_explicit(&_x, memory_order_acquire) + #define HV_SPINLOCK_RELEASE(_x) atomic_flag_clear_explicit(memory_order_release) + #endif +#endif +#ifndef hv_atomic_bool + #define hv_atomic_bool volatile bool + #define HV_SPINLOCK_ACQUIRE(_x) \ + while (_x) {} \ + _x = true; + #define HV_SPINLOCK_TRY(_x) \ + if (!_x) { \ + _x = true; \ + return true; \ + } else return false; + #define HV_SPINLOCK_RELEASE(_x) (_x = false) +#endif + +#endif // _HEAVY_UTILS_H_ diff --git a/delay_simple/plugin/source/Makefile b/delay_simple/plugin/source/Makefile new file mode 100644 index 0000000..49b8004 --- /dev/null +++ b/delay_simple/plugin/source/Makefile @@ -0,0 +1,24 @@ +NAME = delay_simple + +FILES_DSP = $(wildcard *.cpp) +FILES_DSP += $(wildcard *.c) + +include ../../../dpf/Makefile.plugins.mk + +LINK_FLAGS += -lpthread +CFLAGS += -Wno-unused-parameter +CXXFLAGS += -Wno-unused-parameter + + + +TARGETS += lv2_dsp + +TARGETS += vst2 + +TARGETS += vst3 + +TARGETS += jack + + + +all: $(TARGETS) \ No newline at end of file diff --git a/dpf b/dpf new file mode 160000 index 0000000..ac803f5 --- /dev/null +++ b/dpf @@ -0,0 +1 @@ +Subproject commit ac803f5991e1adac6581658a346250d4246e59b6 diff --git a/dpf_delay_simple.json b/dpf_delay_simple.json new file mode 100644 index 0000000..c68a797 --- /dev/null +++ b/dpf_delay_simple.json @@ -0,0 +1,17 @@ +{ + "dpf": { + "dpf_path": "../dpf", + "project": true, + "description": "Simple Delay", + "maker": "Wasted Audio", + "license": "ISC", + "midi_input": 0, + "midi_output": 0, + "plugin_formats": [ + "lv2_dsp", + "vst2", + "vst3", + "jack" + ] + } +} diff --git a/dpf_delay_simple.pd b/dpf_delay_simple.pd new file mode 100644 index 0000000..a425560 --- /dev/null +++ b/dpf_delay_simple.pd @@ -0,0 +1,39 @@ +#N canvas 295 300 807 373 12; +#X obj 88 247 dac~; +#X obj 89 39 adc~; +#X obj 373 160 s dtime1; +#X obj 376 133 hsl 128 15 0 5000 0 0 empty empty empty -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 449 192 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 523 250 hsl 128 15 0 0.95 0 0 empty empty empty -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 446 219 s dvol1; +#X obj 520 277 s dfeed1; +#X obj 100 140 *~; +#X obj 156 112 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 376 64 s dryvol; +#X obj 379 37 hsl 128 15 0 1 0 0 empty empty empty -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 99 169 examples/delay_simple 1; +#X obj 153 84 r Gain @hv_param 0 1 0.75; +#X obj 376 8 r Dry_Volume @hv_param 0 1 0.75; +#X obj 373 104 r Delay_Time @hv_param 0 5000 500; +#X obj 446 163 r Wet_Volume @hv_param 0 1 0.75; +#X obj 520 221 r Delay_Feedback @hv_param 0 1 0.25; +#X connect 1 0 8 0; +#X connect 1 1 8 0; +#X connect 3 0 2 0; +#X connect 4 0 6 0; +#X connect 5 0 7 0; +#X connect 8 0 12 0; +#X connect 9 0 8 1; +#X connect 11 0 10 0; +#X connect 12 0 0 0; +#X connect 12 0 0 1; +#X connect 13 0 9 0; +#X connect 14 0 11 0; +#X connect 15 0 3 0; +#X connect 16 0 4 0; +#X connect 17 0 5 0;