/*
* Klang – a node+text-based synthesizer library
*
* This file is part of the *wellen* library (https://github.com/dennisppaul/wellen).
* Copyright (c) 2022 Dennis P Paul.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KlangMath_hpp
#define KlangMath_hpp
#include <float.h>
#include "Klang.hpp"
#ifndef PI
#define PI 3.14159265358979323846
#endif
#ifndef TWO_PI
#define TWO_PI 6.28318530717958647693
#endif
#ifndef M_TWO_PI
#define M_TWO_PI 6.28318530717958647693
#endif
#ifndef HALF_PI
#define HALF_PI 1.57079632679489661923
#endif
#ifdef __cplusplus
extern "C" {
#endif
float klang_math_sin(float r);
float klang_math_cos(float r);
float klang_math_tan(float r);
float klang_math_sinh(float x);
float klang_math_cosh(float x);
float klang_math_tanh(float x);
float klang_math_fast_sqrt(float x);
#ifdef __cplusplus
}
#endif
namespace klang {
class KlangMath {
public:
static inline uint8_t LO_16i(uint16_t i) {
return (i & 0xFF);
}
static inline uint8_t HI_16i(uint16_t i) {
return ((i >> 8) & 0xFF);
}
static inline uint16_t INT_16i(uint8_t pHighByte, uint8_t pLowByte) {
return (pHighByte << 8) + pLowByte;
}
static inline float FLOAT_32(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
float output;
*((uint8_t*)(&output) + 0) = b0;
*((uint8_t*)(&output) + 1) = b1;
*((uint8_t*)(&output) + 2) = b2;
*((uint8_t*)(&output) + 3) = b3;
return output;
}
static inline float FLOAT_32(uint8_t* pBytes) {
float output;
*((uint8_t*)(&output) + 0) = pBytes[0];
*((uint8_t*)(&output) + 1) = pBytes[1];
*((uint8_t*)(&output) + 2) = pBytes[2];
*((uint8_t*)(&output) + 3) = pBytes[3];
return output;
}
static inline float FLOAT_32(uint8_t* pBytes, uint32_t pOffset) {
float output;
*((uint8_t*)(&output) + 0) = pBytes[0 + pOffset];
*((uint8_t*)(&output) + 1) = pBytes[1 + pOffset];
*((uint8_t*)(&output) + 2) = pBytes[2 + pOffset];
*((uint8_t*)(&output) + 3) = pBytes[3 + pOffset];
return output;
}
static inline uint8_t* FLOAT_BYTES_32(float& pFloat) {
return (uint8_t*)&pFloat;
}
/**
* wraps an index to buffer length. buffer length is required to be *power of 2*
*/
static inline float wrap_float_index(const float pFloatIndex, const uint16_t pBufferLength) {
const uint16_t mInt = (uint16_t)pFloatIndex;
const float mFrac = pFloatIndex - mInt;
const uint16_t mNextInt = mInt & (pBufferLength - 1);
return mNextInt + mFrac;
}
/**
* returns a random number between -1 ... 1
*/
static float random() {
// TODO replace with mapping, without division
return ((float)xorshift32() / UINT32_MAX) * 2 - 1;
}
/**
* returns a random number between 0 ... 1
*/
static float random_normalized() {
// TODO replace with mapping, without division
return ((float)xorshift32() / UINT32_MAX);
}
/* xorshift32 ( ref: https://en.wikipedia.org/wiki/Xorshift ) */
static uint32_t x32Seed;
static uint32_t xorshift32() {
x32Seed ^= x32Seed << 13;
x32Seed ^= x32Seed >> 17;
x32Seed ^= x32Seed << 5;
return x32Seed;
}
static uint8_t power_of_2(uint16_t p) {
uint8_t c = 0;
while ((p /= 2) > 0) {
c++;
}
return c;
}
static uint16_t shift(float pValue, uint8_t pShifts) {
for (uint8_t i = 0; i < pShifts; i++) {
pValue *= 2;
}
return (uint16_t)pValue;
}
static float clamp(float pValue, float pMin, float pMax) {
return pValue > pMax ? pMax : (pValue < pMin ? pMin : pValue);
}
static float clamp(float pValue) {
return pValue > SIGNAL_MAX ? SIGNAL_MAX : (pValue < SIGNAL_MIN ? SIGNAL_MIN : pValue);
}
static float clamp(uint8_t pValue, uint8_t pMin, uint8_t pMax) {
return pValue > pMax ? pMax : (pValue < pMin ? pMin : pValue);
}
static int clamp127(uint8_t pValue) {
return pValue > 127 ? 127 : pValue;
}
static float clamp_signal(float pValue) {
return pValue > SIGNAL_MAX ? SIGNAL_MAX : (pValue < SIGNAL_MIN ? SIGNAL_MIN : pValue);
}
static float lerp(float v0, float v1, float t) {
return v0 + t * (v1 - v0);
}
static float map(float value, float inputMin, float inputMax, float outputMin, float outputMax) {
return ((value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin);
}
static int16_t map_i16(int16_t value, int16_t inputMin, int16_t inputMax, int16_t outputMin, int16_t outputMax) {
return ((value - inputMin) * (outputMax - outputMin) / (inputMax - inputMin) + outputMin);
}
// #define USE_FMOD_WITH_WHILE TRUE
static float mod(float a, float b) {
#if USE_FMOD_WITH_WHILE
while (a >= b) {
a -= b;
}
return a;
#else
return a >= b ? (a-b*int(a/b)) : a;
// return a >= b ? fmodf(a, b) : a;
// return input >= ceil ? input % ceil : input; // from https://stackoverflow.com/questions/33333363/built-in-mod-vs-custom-mod-function-improve-the-performance-of-modulus-op
#endif
}
static constexpr float MIDI_NOTE_CONVERSION_BASE_FREQUENCY = 440.0;
static const uint8_t NOTE_OFFSET = 69;
static float note_to_frequency(uint8_t pMidiNote) {
return MIDI_NOTE_CONVERSION_BASE_FREQUENCY * pow(2, ((pMidiNote - NOTE_OFFSET) / 12.0));
}
};
/**
* @file SimplexNoise.h
* @brief A Perlin Simplex Noise C++ Implementation (1D, 2D, 3D, 4D).
*
* Copyright (c) 2014-2015 Sebastien Rombauts (sebastien.rombauts@gmail.com)
*
* Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
* or copy at http://opensource.org/licenses/MIT)
*/
/**
* @brief A Perlin Simplex Noise C++ Implementation (1D, 2D, 3D, 4D).
*/
class SimplexNoise {
public:
// 1D Perlin simplex noise
static float noise(float x);
// 2D Perlin simplex noise
static float noise(float x, float y);
static float noise_normalized(float x) { return noise(x) * 0.5 + 0.5; }
static float noise_normalized(float x, float y) { return noise(x, y) * 0.5 + 0.5; }
};
} // namespace klang
#endif /* KlangMath_hpp */