/** * 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 #define Context(_c) static_cast(_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(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(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(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; }