added a new platformIO test project

This commit is contained in:
2025-11-25 16:52:38 +01:00
parent ec3a779ce4
commit 8d66fa86db
33 changed files with 4977 additions and 0 deletions

5
test-pico-io/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
test-pico-io/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@ -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<HeavyContext *>(context);
const hv_uint32_t numBytes = sizeof(ReceiverMessagePair) + msg_getSize(msg) - sizeof(HvMessage);
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(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<ReceiverMessagePair *>(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<HeavyContext *>(c)->getTableForHash(tableHash);
}
void _hv_scheduleMessageForReceiver(HeavyContextInterface *c, hv_uint32_t receiverHash, HvMessage *m) {
hv_assert(c != nullptr);
reinterpret_cast<HeavyContext *>(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<HeavyContext *>(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

View File

@ -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_

View File

@ -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_

View File

@ -0,0 +1,265 @@
/**
* Copyright (c) 2025 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_temp_a2960967.hpp"
#include <new>
#define Context(_c) static_cast<Heavy_temp_a2960967 *>(_c)
/*
* C Functions
*/
extern "C" {
HV_EXPORT HeavyContextInterface *hv_temp_a2960967_new(double sampleRate) {
// allocate aligned memory
void *ptr = hv_malloc(sizeof(Heavy_temp_a2960967));
// ensure non-null
if (!ptr) return nullptr;
// call constructor
new(ptr) Heavy_temp_a2960967(sampleRate);
return Context(ptr);
}
HV_EXPORT HeavyContextInterface *hv_temp_a2960967_new_with_options(double sampleRate,
int poolKb, int inQueueKb, int outQueueKb) {
// allocate aligned memory
void *ptr = hv_malloc(sizeof(Heavy_temp_a2960967));
// ensure non-null
if (!ptr) return nullptr;
// call constructor
new(ptr) Heavy_temp_a2960967(sampleRate, poolKb, inQueueKb, outQueueKb);
return Context(ptr);
}
HV_EXPORT void hv_temp_a2960967_free(HeavyContextInterface *instance) {
// call destructor
Context(instance)->~Heavy_temp_a2960967();
// free memory
hv_free(instance);
}
} // extern "C"
/*
* Class Functions
*/
Heavy_temp_a2960967::Heavy_temp_a2960967(double sampleRate, int poolKb, int inQueueKb, int outQueueKb)
: HeavyContext(sampleRate, poolKb, inQueueKb, outQueueKb) {
numBytes += sPhasor_k_init(&sPhasor_dLinxAVa, 440.0f, sampleRate);
}
Heavy_temp_a2960967::~Heavy_temp_a2960967() {
// nothing to free
}
HvTable *Heavy_temp_a2960967::getTableForHash(hv_uint32_t tableHash) {
return nullptr;
}
void Heavy_temp_a2960967::scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) {
switch (receiverHash) {
default: return;
}
}
int Heavy_temp_a2960967::getParameterInfo(int index, HvParameterInfo *info) {
if (info != nullptr) {
switch (index) {
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 0;
}
/*
* Send Function Implementations
*/
/*
* Context Process Implementation
*/
int Heavy_temp_a2960967::process(float **inputBuffers, float **outputBuffers, int n) {
while (hLp_hasData(&inQueue)) {
hv_uint32_t numBytes = 0;
ReceiverMessagePair *p = reinterpret_cast<ReceiverMessagePair *>(hLp_getReadBuffer(&inQueue, &numBytes));
hv_assert(numBytes >= sizeof(ReceiverMessagePair));
scheduleMessageForReceiver(p->receiverHash, &p->msg);
hLp_consume(&inQueue);
}
sendBangToReceiver(0xDD21C0EB); // send to __hv_bang~ on next cycle
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, Bf4;
// input and output vars
hv_bufferf_t O0, O1;
// 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);
}
// zero output buffers
__hv_zero_f(VOf(O0));
__hv_zero_f(VOf(O1));
// process all signal functions
__hv_phasor_k_f(&sPhasor_dLinxAVa, VOf(Bf0));
__hv_var_k_f(VOf(Bf1), 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f);
__hv_sub_f(VIf(Bf0), VIf(Bf1), VOf(Bf1));
__hv_abs_f(VIf(Bf1), VOf(Bf1));
__hv_var_k_f(VOf(Bf0), 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f);
__hv_sub_f(VIf(Bf1), VIf(Bf0), VOf(Bf0));
__hv_var_k_f(VOf(Bf1), 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f, 6.283185307179586f);
__hv_mul_f(VIf(Bf0), VIf(Bf1), VOf(Bf1));
__hv_mul_f(VIf(Bf1), VIf(Bf1), VOf(Bf0));
__hv_mul_f(VIf(Bf1), VIf(Bf0), VOf(Bf2));
__hv_mul_f(VIf(Bf2), VIf(Bf0), VOf(Bf0));
__hv_var_k_f(VOf(Bf3), 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f, 0.007833333333333f);
__hv_var_k_f(VOf(Bf4), -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f, -0.166666666666667f);
__hv_fma_f(VIf(Bf2), VIf(Bf4), VIf(Bf1), VOf(Bf1));
__hv_fma_f(VIf(Bf0), VIf(Bf3), VIf(Bf1), VOf(Bf1));
__hv_add_f(VIf(Bf1), VIf(O0), VOf(O0));
__hv_add_f(VIf(Bf1), 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_temp_a2960967::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 0 channel(s)
float **const bIn = NULL;
// define the heavy output buffer for 2 channel(s)
float **const bOut = reinterpret_cast<float **>(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_temp_a2960967::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 0 channel(s), uninterleave
float *const bIn = NULL;
// define the heavy output buffer for 2 channel(s)
float *const bOut = reinterpret_cast<float *>(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;
}

View File

@ -0,0 +1,78 @@
/**
* Copyright (c) 2025 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_TEMP_A2960967_H_
#define _HEAVY_TEMP_A2960967_H_
#include "HvHeavy.h"
#ifdef __cplusplus
extern "C" {
#endif
#if HV_APPLE
#pragma mark - Heavy Context
#endif
/**
* Creates a new patch instance.
* Sample rate should be positive and in Hertz, e.g. 44100.0.
*/
HeavyContextInterface *hv_temp_a2960967_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_temp_a2960967_new_with_options(double sampleRate, int poolKb, int inQueueKb, int outQueueKb);
/**
* Free the patch instance.
*/
void hv_temp_a2960967_free(HeavyContextInterface *instance);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _HEAVY_TEMP_A2960967_H_

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2025 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_TEMP_A2960967_HPP_
#define _HEAVY_CONTEXT_TEMP_A2960967_HPP_
// object includes
#include "HeavyContext.hpp"
#include "HvSignalPhasor.h"
#include "HvSignalVar.h"
#include "HvMath.h"
class Heavy_temp_a2960967 : public HeavyContext {
public:
Heavy_temp_a2960967(double sampleRate, int poolKb=10, int inQueueKb=2, int outQueueKb=0);
~Heavy_temp_a2960967();
const char *getName() override { return "temp_a2960967"; }
int getNumInputChannels() override { return 0; }
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;
private:
HvTable *getTableForHash(hv_uint32_t tableHash) override;
void scheduleMessageForReceiver(hv_uint32_t receiverHash, HvMessage *m) override;
// static sendMessage functions
// objects
SignalPhasor sPhasor_dLinxAVa;
};
#endif // _HEAVY_CONTEXT_TEMP_A2960967_HPP_

View File

@ -0,0 +1,347 @@
/**
* 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_APPLE
#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_APPLE
#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_APPLE
#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_sendMessageToReceiverFF(
HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, double data1, double data2) {
hv_assert(c != nullptr);
hv_assert(delayMs >= 0.0);
const int numElem = (int) 2;
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));
msg_setFloat(m, 0, (float) data1);
msg_setFloat(m, 1, (float) data2);
return c->sendMessageToReceiver(receiverHash, delayMs, m);
}
HV_EXPORT bool hv_sendMessageToReceiverFFF(
HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, double data1, double data2, double data3) {
hv_assert(c != nullptr);
hv_assert(delayMs >= 0.0);
const int numElem = (int) 3;
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));
msg_setFloat(m, 0, (float) data1);
msg_setFloat(m, 1, (float) data2);
msg_setFloat(m, 2, (float) data3);
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_APPLE
#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

View File

@ -0,0 +1,433 @@
/**
* 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 fixed formatted message of two floats 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_sendMessageToReceiverFF(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, double data1, double data2);
/**
* Sends a fixed formatted message of three floats 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_sendMessageToReceiverFFF(HeavyContextInterface *c, hv_uint32_t receiverHash, double delayMs, double data1, double data2, double data3);
/**
* 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_

View File

@ -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

View File

@ -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 <xmmintrin.h>
#define hv_sfence() _mm_sfence()
#elif __arm__ || HV_SIMD_NEON
#if __ARM_ACLE
#include <arm_acle.h>
// 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__) && (__ARM_ARCH >= 7)
#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);
}

View File

@ -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_

View File

@ -0,0 +1,736 @@
/**
* 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[15]),
hv_pow_f(b[6], b[14]),
hv_pow_f(b[5], b[13]),
hv_pow_f(b[4], b[12]),
hv_pow_f(b[3], b[11]),
hv_pow_f(b[2], b[10]),
hv_pow_f(b[1], b[9]),
hv_pow_f(b[0], b[8]));
#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_eq_f(hv_bInf_t bIn0, hv_bInf_t bIn1, hv_bOutf_t bOut) {
#if HV_SIMD_AVX
*bOut = _mm256_cmp_ps(bIn0, bIn1, _CMP_EQ_OQ);
#elif HV_SIMD_SSE
*bOut = _mm_cmpeq_ps(bIn0, bIn1);
#elif HV_SIMD_NEON
*bOut = vreinterpretq_f32_u32(vceqq_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_

View File

@ -0,0 +1,200 @@
/**
* 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: {
union { float f; hv_uint32_t u; } fhash;
fhash.f = msg_getFloat(m,i);
return fhash.u;
}
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;
}

View File

@ -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_

View File

@ -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;
}
}

View File

@ -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 _MESSAGE_POOL_H_
#define _MESSAGE_POOL_H_
#include "HvUtils.h"
#ifdef HV_MP_NUM_MESSAGE_LISTS
#define MP_NUM_MESSAGE_LISTS HV_MP_NUM_MESSAGE_LISTS
#else // HV_MP_NUM_MESSAGE_LISTS
#define MP_NUM_MESSAGE_LISTS 4
#endif // HV_MP_NUM_MESSAGE_LISTS
#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_

View File

@ -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;
}

View File

@ -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_

View File

@ -0,0 +1,141 @@
/**
* 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 "HvSignalPhasor.h"
#define HV_PHASOR_2_32 4294967296.0
#if HV_SIMD_AVX
static void sPhasor_updatePhase(SignalPhasor *o, float p) {
o->phase = _mm256_set1_ps(p+1.0f); // o->phase is in range [1,2]
#elif HV_SIMD_SSE
static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) {
o->phase = _mm_set1_epi32(p);
#elif HV_SIMD_NEON
static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) {
o->phase = vdupq_n_u32(p);
#else // HV_SIMD_NONE
static void sPhasor_updatePhase(SignalPhasor *o, hv_uint32_t p) {
o->phase = p;
#endif
}
// input phase is in the range of [0,1]. It is independent of o->phase.
#if HV_SIMD_AVX
static void sPhasor_k_updatePhase(SignalPhasor *o, float p) {
o->phase = _mm256_set_ps(
p+1.0f+7.0f*o->step.f2sc, p+1.0f+6.0f*o->step.f2sc,
p+1.0f+5.0f*o->step.f2sc, p+1.0f+4.0f*o->step.f2sc,
p+1.0f+3.0f*o->step.f2sc, p+1.0f+2.0f*o->step.f2sc,
p+1.0f+o->step.f2sc, p+1.0f);
// ensure that o->phase is still in range [1,2]
o->phase = _mm256_or_ps(_mm256_andnot_ps(
_mm256_set1_ps(-INFINITY), o->phase), _mm256_set1_ps(1.0f));
#elif HV_SIMD_SSE
static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) {
o->phase = _mm_set_epi32(3*o->step.s+p, 2*o->step.s+p, o->step.s+p, p);
#elif HV_SIMD_NEON
static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) {
o->phase = (uint32x4_t) {p, o->step.s+p, 2*o->step.s+p, 3*o->step.s+p};
#else // HV_SIMD_NONE
static void sPhasor_k_updatePhase(SignalPhasor *o, hv_uint32_t p) {
o->phase = p;
#endif
}
static void sPhasor_k_updateFrequency(SignalPhasor *o, float f, double r) {
#if HV_SIMD_AVX
o->step.f2sc = (float) (f/r);
o->inc = _mm256_set1_ps((float) (8.0f*f/r));
sPhasor_k_updatePhase(o, o->phase[0]);
#elif HV_SIMD_SSE
o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r));
o->inc = _mm_set1_epi32(4*o->step.s);
const hv_uint32_t *const p = (hv_uint32_t *) &o->phase;
sPhasor_k_updatePhase(o, p[0]);
#elif HV_SIMD_NEON
o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r));
o->inc = vdupq_n_s32(4*o->step.s);
sPhasor_k_updatePhase(o, vgetq_lane_u32(o->phase, 0));
#else // HV_SIMD_NONE
o->step.s = (hv_int32_t) (f*(HV_PHASOR_2_32/r));
o->inc = o->step.s;
// no need to update phase
#endif
}
hv_size_t sPhasor_init(SignalPhasor *o, double samplerate) {
#if HV_SIMD_AVX
o->phase = _mm256_set1_ps(1.0f);
o->inc = _mm256_setzero_ps();
o->step.f2sc = (float) (1.0/samplerate);
#elif HV_SIMD_SSE
o->phase = _mm_setzero_si128();
o->inc = _mm_setzero_si128();
o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate);
#elif HV_SIMD_NEON
o->phase = vdupq_n_u32(0);
o->inc = vdupq_n_s32(0);
o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate);
#else // HV_SIMD_NONE
o->phase = 0;
o->inc = 0;
o->step.f2sc = (float) (HV_PHASOR_2_32/samplerate);
#endif
return 0;
}
void sPhasor_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m) {
if (letIn == 1) {
if (msg_isFloat(m,0)) {
float p = msg_getFloat(m,0);
while (p < 0.0f) p += 1.0f; // wrap phase to [0,1]
while (p > 1.0f) p -= 1.0f;
#if HV_SIMD_AVX
sPhasor_updatePhase(o, p);
#else // HV_SIMD_SSE || HV_SIMD_NEON || HV_SIMD_NONE
sPhasor_updatePhase(o, (hv_uint32_t) (p * HV_PHASOR_2_32));
#endif
}
}
}
hv_size_t sPhasor_k_init(SignalPhasor *o, float frequency, double samplerate) {
__hv_zero_i((hv_bOuti_t) &o->phase);
sPhasor_k_updateFrequency(o, frequency, samplerate);
return 0;
}
void sPhasor_k_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m) {
if (msg_isFloat(m,0)) {
switch (letIn) {
case 0: sPhasor_k_updateFrequency(o, msg_getFloat(m,0), hv_getSampleRate(_c)); break;
case 1: {
float p = msg_getFloat(m,0);
while (p < 0.0f) p += 1.0f; // wrap phase to [0,1]
while (p > 1.0f) p -= 1.0f;
#if HV_SIMD_AVX
sPhasor_k_updatePhase(o, p);
#else // HV_SIMD_SSE || HV_SIMD_NEON || HV_SIMD_NONE
sPhasor_k_updatePhase(o, (hv_uint32_t) (p * HV_PHASOR_2_32));
#endif
break;
}
default: break;
}
}
}

View File

@ -0,0 +1,141 @@
/**
* 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_PHASOR_H_
#define _HEAVY_SIGNAL_PHASOR_H_
#include "HvHeavyInternal.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SignalPhasor {
#if HV_SIMD_AVX
__m256 phase; // current phase
__m256 inc; // phase increment
#elif HV_SIMD_SSE
__m128i phase;
__m128i inc;
#elif HV_SIMD_NEON
uint32x4_t phase;
int32x4_t inc;
#else // HV_SIMD_NONE
hv_uint32_t phase;
hv_int32_t inc;
#endif
union {
float f2sc; // float to step conversion (used for __phasor~f)
hv_int32_t s; // step value (used for __phasor_k~f)
} step;
} SignalPhasor;
hv_size_t sPhasor_init(SignalPhasor *o, double samplerate);
hv_size_t sPhasor_k_init(SignalPhasor *o, float frequency, double samplerate);
void sPhasor_k_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m);
void sPhasor_onMessage(HeavyContextInterface *_c, SignalPhasor *o, int letIn, const HvMessage *m);
static inline void __hv_phasor_f(SignalPhasor *o, hv_bInf_t bIn, hv_bOutf_t bOut) {
#if HV_SIMD_AVX
__m256 p = _mm256_mul_ps(bIn, _mm256_set1_ps(o->step.f2sc)); // a b c d e f g h
__m256 z = _mm256_setzero_ps();
// http://stackoverflow.com/questions/11906814/how-to-rotate-an-sse-avx-vector
__m256 a = _mm256_permute_ps(p, _MM_SHUFFLE(2,1,0,3)); // d a b c h e f g
__m256 b = _mm256_permute2f128_ps(a, a, 0x01); // h e f g d a b c
__m256 c = _mm256_blend_ps(a, b, 0x10); // d a b c d e f g
__m256 d = _mm256_blend_ps(c, z, 0x01); // 0 a b c d e f g
__m256 e = _mm256_add_ps(p, d); // a (a+b) (b+c) (c+d) (d+e) (e+f) (f+g) (g+h)
__m256 f = _mm256_permute_ps(e, _MM_SHUFFLE(1,0,3,2)); // (b+c) (c+d) a (a+b) (f+g) (g+h) (d+e) (e+f)
__m256 g = _mm256_permute2f128_ps(f, f, 0x01); // (f+g) (g+h) (d+e) (e+f) (b+c) (c+d) a (a+b)
__m256 h = _mm256_blend_ps(f, g, 0x33); // (b+c) (c+d) a (a+b) (b+c) (c+d) (d+e) (e+f)
__m256 i = _mm256_blend_ps(h, z, 0x03); // 0 0 a (a+b) (b+c) (c+d) (d+e) (e+f)
__m256 j = _mm256_add_ps(e, i); // a (a+b) (a+b+c) (a+b+c+d) (b+c+d+e) (c+d+e+f) (d+e+f+g) (e+f+g+h)
__m256 k = _mm256_permute2f128_ps(j, z, 0x02); // 0 0 0 0 a (a+b) (a+b+c) (a+b+c+d) (b+c+d+e)
__m256 m = _mm256_add_ps(j, k); // a (a+b) (a+b+c) (a+b+c+d) (a+b+c+d+e) (a+b+c+d+e+f) (a+b+c+d+e+f+g) (a+b+c+d+e+f+g+h)
__m256 n = _mm256_or_ps(_mm256_andnot_ps(
_mm256_set1_ps(-INFINITY),
_mm256_add_ps(o->phase, m)),
_mm256_set1_ps(1.0f));
*bOut = _mm256_sub_ps(n, _mm256_set1_ps(1.0f));
__m256 x = _mm256_permute_ps(n, _MM_SHUFFLE(3,3,3,3));
o->phase = _mm256_permute2f128_ps(x, x, 0x11);
#elif HV_SIMD_SSE
__m128i p = _mm_cvtps_epi32(_mm_mul_ps(bIn, _mm_set1_ps(o->step.f2sc))); // convert frequency to step
p = _mm_add_epi32(p, _mm_slli_si128(p, 4)); // add incremental steps to phase (prefix sum)
p = _mm_add_epi32(p, _mm_slli_si128(p, 8)); // http://stackoverflow.com/questions/10587598/simd-prefix-sum-on-intel-cpu?rq=1
p = _mm_add_epi32(o->phase, p);
*bOut = _mm_sub_ps(_mm_castsi128_ps(
_mm_or_si128(_mm_srli_epi32(p, 9),
_mm_set_epi32(0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000))),
_mm_set1_ps(1.0f));
o->phase = _mm_shuffle_epi32(p, _MM_SHUFFLE(3,3,3,3));
#elif HV_SIMD_NEON
int32x4_t p = vcvtq_s32_f32(vmulq_n_f32(bIn, o->step.f2sc));
p = vaddq_s32(p, vextq_s32(vdupq_n_s32(0), p, 3)); // http://stackoverflow.com/questions/11259596/arm-neon-intrinsics-rotation
p = vaddq_s32(p, vextq_s32(vdupq_n_s32(0), p, 2));
uint32x4_t pp = vaddq_u32(o->phase, vreinterpretq_u32_s32(p));
*bOut = vsubq_f32(vreinterpretq_f32_u32(vorrq_u32(vshrq_n_u32(pp, 9), vdupq_n_u32(0x3F800000))), vdupq_n_f32(1.0f));
o->phase = vdupq_n_u32(pp[3]);
#else // HV_SIMD_NONE
union { float f; hv_uint32_t u; } uphase;
uphase.u = (o->phase >> 9) | 0x3F800000;
*bOut = uphase.f - 1.0f;
o->phase += ((int) (bIn * o->step.f2sc));
#endif
}
static inline void __hv_phasor_k_f(SignalPhasor *o, hv_bOutf_t bOut) {
#if HV_SIMD_AVX
*bOut = _mm256_sub_ps(o->phase, _mm256_set1_ps(1.0f));
o->phase = _mm256_or_ps(_mm256_andnot_ps(
_mm256_set1_ps(-INFINITY),
_mm256_add_ps(o->phase, o->inc)),
_mm256_set1_ps(1.0f));
#elif HV_SIMD_SSE
*bOut = _mm_sub_ps(_mm_castsi128_ps(
_mm_or_si128(_mm_srli_epi32(o->phase, 9),
_mm_set_epi32(0x3F800000, 0x3F800000, 0x3F800000, 0x3F800000))),
_mm_set1_ps(1.0f));
o->phase = _mm_add_epi32(o->phase, o->inc);
#elif HV_SIMD_NEON
*bOut = vsubq_f32(vreinterpretq_f32_u32(
vorrq_u32(vshrq_n_u32(o->phase, 9),
vdupq_n_u32(0x3F800000))),
vdupq_n_f32(1.0f));
o->phase = vaddq_u32(o->phase, vreinterpretq_u32_s32(o->inc));
#else // HV_SIMD_NONE
union { float f; hv_uint32_t u; } uphase;
uphase.u = (o->phase >> 9) | 0x3F800000;
*bOut = uphase.f - 1.0f;
o->phase += o->inc;
#endif
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _HEAVY_SIGNAL_PHASOR_H_

View File

@ -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);
}
}

View File

@ -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_

View File

@ -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<<HV_N_SIMD)-1)) == 0) {
if (newSize > 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));
}
}

View File

@ -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_

View File

@ -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;
}

View File

@ -0,0 +1,319 @@
/**
* 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
// type definitions
#include <stdint.h>
#include <stdbool.h>
#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 <immintrin.h>
#elif HV_SIMD_NEON
#include <arm_neon.h>
#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 <string.h>
#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 <malloc.h>
#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 <mm_malloc.h>
#define hv_malloc(_n) _mm_malloc(_n, 32)
#define hv_free(x) _mm_free(x)
#elif HV_SIMD_SSE
#include <mm_malloc.h>
#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 <alloca.h>
#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 <assert.h>
#define hv_assert(e) assert(e)
// Export and Inline
#if HV_WIN
#define HV_EXPORT __declspec(dllexport)
#ifndef __cplusplus // MSVC doesn't like redefining "inline" keyword
#define inline __inline
#endif
#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 <math.h>
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 <intrin.h>
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 <windows.h>
#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 <atomic>
#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(<stdatomic.h>)
#include <stdatomic.h>
#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_

46
test-pico-io/lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@ -0,0 +1,15 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:pico]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = rpipico2
framework = arduino
board_build.core = earlephilhower

37
test-pico-io/src/main.cpp Normal file
View File

@ -0,0 +1,37 @@
#include <Arduino.h>
#include "Heavy_temp_a2960967.hpp"
// Erzeuge eine globale Heavy-Instanz
Heavy_temp_a2960967* hv = nullptr;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Heavy DSP Test startet...");
// Heavy-Context initialisieren (44.1 kHz z. B.)
hv = new Heavy_temp_a2960967(44100.0);
if (hv) {
Serial.println("Heavy context erfolgreich erstellt!");
} else {
Serial.println("Fehler: Heavy context konnte nicht erstellt werden.");
}
}
void loop() {
if (!hv) return;
// Testausgabe mit einem leeren Audiobuffer
const int blockSize = 64;
float output[2 * blockSize]; // 2 Kanäle interleaved
// DSP verarbeiten
hv->processInlineInterleaved(nullptr, output, blockSize);
// Optional: etwas ausgeben (z. B. ein Samplewert)
Serial.print("Sample L[0]: ");
Serial.println(output[0]);
delay(100);
}

11
test-pico-io/test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html