diff --git a/library.properties b/library.properties index 0afb0a7..e765fe0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ML SynthTools -version=1.0.1 +version=1.1.0 author=Marcel Licence maintainer=Marcel Licence sentence=Synthesizer Tools diff --git a/src/ml_lfo.cpp b/src/ml_lfo.cpp new file mode 100644 index 0000000..6beed71 --- /dev/null +++ b/src/ml_lfo.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_lfo.cpp + * @author Marcel Licence + * @date 01.09.2023 + * + * @brief LFO implementation + * + * @see little demo: https://youtu.be/hqK_U22Jha8 (more details coming soon) + */ + + +#ifdef __CDT_PARSER__ +#include +#endif + + +#include +#include +#include + + +ML_LFO::ML_LFO(float sample_rate, float *buffer, uint32_t sample_count) +{ + this->sample_rate = sample_rate; + frequency = 1.0f; + phase = 0.0f; + this->buffer = buffer; + bufferSize = sample_count; + memset(buffer, 0, sizeof(float) * sample_count); +} + +void ML_LFO::Process(const float *frequency_in, float *buffer, uint32_t len) +{ + for (uint32_t n = 0; n < len; n++) + { + const float omega = frequency_in[n] * 2.0f * M_PI / (sample_rate); + + phase += omega; + if (phase >= 2.0f * M_PI) + { + phase -= 2.0f * M_PI; + } + buffer[n] = sinf(phase); + } +} + +void ML_LFO::Process(const float *frequency_in, uint32_t len) +{ + for (uint32_t n = 0; n < len; n++) + { + const float omega = frequency_in[n] * 2.0f * M_PI / (sample_rate); + + phase += omega; + if (phase >= 2.0f * M_PI) + { + phase -= 2.0f * M_PI; + } + buffer[n] = sinf(phase); + } +} + +void ML_LFO::Process(uint32_t len) +{ + const float omega = frequency * 2.0f * M_PI / (sample_rate); + + for (uint32_t n = 0; n < len; n++) + { + phase += omega; + if (phase >= 2.0f * M_PI) + { + phase -= 2.0f * M_PI; + } + buffer[n] = sinf(phase); + } +} + +void ML_LFO::setFrequency(float frequency) +{ + this->frequency = frequency; +} + +void ML_LFO::setPhase(float phase) +{ + this->phase = phase; +} + diff --git a/src/ml_lfo.h b/src/ml_lfo.h new file mode 100644 index 0000000..7d8b416 --- /dev/null +++ b/src/ml_lfo.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_lfo.h + * @author Marcel Licence + * @date 01.09.2023 + * + * @brief LFO declarations + * + * @see little demo: https://youtu.be/hqK_U22Jha8 (more details coming soon) + */ + + +#ifndef SRC_ML_LFO_H_ +#define SRC_ML_LFO_H_ + + +#include + + +class ML_LFO +{ +public: + ML_LFO(float sample_rate, float *buffer, uint32_t sample_count); + ~ML_LFO() {}; + void Process(const float *frequency_in, float *buffer, uint32_t len); + void Process(const float *frequency_in, uint32_t len); + void Process(uint32_t len); + void setFrequency(float speed); + void setPhase(float phase); + +private: + float sample_rate; + float frequency; + float phase; + float *buffer; + float bufferSize; +}; + + +#endif /* SRC_ML_LFO_H_ */ + diff --git a/src/ml_phaser.cpp b/src/ml_phaser.cpp new file mode 100644 index 0000000..d5b28f1 --- /dev/null +++ b/src/ml_phaser.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_phaser.cpp + * @author Marcel Licence + * @date 20.12.2022 + * + * @brief This is a simple implementation of a stereo s16 phaser line + * - level adjustable + * - feedback + * - length adjustable + * @see first peak: https://youtu.be/Kac9AB02BcQ + * @see little demo: https://youtu.be/hqK_U22Jha8 + */ + + +#ifdef __CDT_PARSER__ +#include +#endif + + +#include +#include +#include + +#ifndef ARDUINO +#include +#include +#include +#include +#endif + + +/* + * module variables + */ +static int16_t *phaserLine_l; +static int16_t *phaserLine_r; + +static float phaserToMix = 0; +static float phaserInLvl = 1.0f; +static float phaserAuto = 1.0f; +static float phaserDepth = 1.0f; +static uint32_t phaserLenMax = 0; +static uint32_t phaserLen = 11098; + + +static struct allpass_s ap0 = +{ + NULL, + 0, + 0.7f, + (int)(0) +}; + + +static uint32_t AllpassInit(float *buffer, int i, struct allpass_s *ap, int len) +{ + ap->buf = &buffer[i]; + ap->p = 0.0f; + ap->g = 0.00025f; + ap->lim = len; + return len; +} + + + +static inline void Do_AllpassPhase(struct allpass_s *ap, const float *inSample, const float *len, float *outSample, int buffLen) +{ + struct allpass_s ap2; + memcpy(&ap2, ap, sizeof(ap2)); + + for (int n = 0; n < buffLen; n++) + { + float readback = ap2.buf[(uint32_t)ap2.p]; + readback += (-ap2.g) * inSample[n]; + const float newV = readback * ap2.g + inSample[n]; + ap2.buf[(uint32_t)ap2.p] = newV; + ap2.p++; + if (ap2.p >= len[n]) + { + ap2.p -= len[n]; + } + outSample[n] = readback; + } + memcpy(ap, &ap2, sizeof(ap2)); +} + + +void Phaser_Init(float *buffer, uint32_t len) +{ + if (buffer == NULL) + { + printf("No memory to initialize Phaser!\n"); + return; + } + else + { + memset(buffer, 0, sizeof(float) * PHASER_BUFFER_SIZE); + } + uint32_t i = 0; + + + i += AllpassInit(buffer, i, &ap0, PHASER_AP0); + + if (i == len) + { + printf("Phaser Init ok\n"); + } + +} + + +void Phaser_Reset(void) +{ + for (uint32_t i = 0; i < phaserLenMax; i++) + { + phaserLine_l[i] = 0; + } + if (phaserLine_r != NULL) + { + for (uint32_t i = 0; i < phaserLenMax; i++) + { + phaserLine_r[i] = 0; + } + } +} + + +void Phaser_Process(const float *in, const float *lfo_in, float *out, int buffLen) +{ + float newsample[buffLen]; + float len1[buffLen]; + float phaserDepth_c = phaserDepth; + float phaserMod_c = 1.0f; + + for (int n = 0; n < buffLen; n++) + { + /* causing errors when lfo_in == 0 */ + len1[n] = 3 + (1 + lfo_in[n]) * phaserMod_c * 48.0f; + } + + memcpy(newsample, in, sizeof(newsample)); + + Do_AllpassPhase(&ap0, newsample, len1, newsample, buffLen); + + for (int n = 0; n < buffLen; n++) + { + out[n] = in[n] - newsample[n] * phaserDepth_c; + } +} + +void Phaser_ProcessHQ(const float *in, const float *lfo_in, float *out, int buffLen) +{ + Phaser_ProcessHQ(in, lfo_in, out, buffLen, phaserDepth, & ap0); +} + +void Phaser_SetInputLevel(uint8_t unused __attribute__((unused)), float value) +{ + phaserInLvl = value; + Status_ValueChangedFloat("Phaser", "InputLevel", value); +} + +void Phaser_SetDepth(uint8_t unused __attribute__((unused)), float value) +{ + phaserDepth = value; + Status_ValueChangedFloat("Phaser", "Depth", value); +} + +void Phaser_SetDepth(uint8_t unused __attribute__((unused)), uint8_t value_u8) +{ + float value = (1.0f / 127.0f) * (float)value_u8; + phaserDepth = value; + Status_ValueChangedFloat("Phaser", "Depth", value); +} + +void Phaser_SetAuto(uint8_t unused __attribute__((unused)), float value) +{ + phaserAuto = value; +} + +void Phaser_SetOutputLevel(uint8_t unused __attribute__((unused)), float value) +{ + phaserToMix = value; + Status_ValueChangedFloat("Phaser", "Level", value); +} + +void Phaser_SetG(uint8_t unused __attribute__((unused)), float value) +{ + ap0.g = value; + Status_ValueChangedFloat("Phaser", "G", value); +} + +void Phaser_SetG(uint8_t unused __attribute__((unused)), uint8_t value_u8) +{ + float value = (1.0f / 127.0f) * (float)value_u8; + ap0.g = value; + Status_ValueChangedFloat("Phaser", "G", value); +} + +void Phaser_SetLength(uint8_t unused __attribute__((unused)), float value) +{ + phaserLen = (uint32_t)(((float)phaserLenMax - 1.0f) * value); +} + diff --git a/src/ml_phaser.h b/src/ml_phaser.h new file mode 100644 index 0000000..b5ea84b --- /dev/null +++ b/src/ml_phaser.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_phaser.h + * @author Marcel Licence + * @date 20.12.2022 + * + * @brief This file contains an implementation of a simple stereo phaser effect + * + * @see first peak: https://youtu.be/Kac9AB02BcQ + * @see little demo: https://youtu.be/hqK_U22Jha8 + */ + + +#ifndef SRC_ML_PHASER_H_ +#define SRC_ML_PHASER_H_ + + +#include + + +#define PHASER_AP0 150 + +#define PHASER_BUFFER_SIZE (PHASER_AP0) + + +struct allpass_s +{ + float *buf; + uint32_t p; + float g; + uint32_t lim; +}; + + +void Phaser_Init(float *buffer, uint32_t len); +void Phaser_Process(const float *in, const float *lfo_in, float *out, int buffLen); +void Phaser_ProcessHQ(const float *in, const float *lfo_in, float *out, int buffLen); +void Phaser_SetInputLevel(uint8_t unused __attribute__((unused)), float value); +void Phaser_SetDepth(uint8_t unused __attribute__((unused)), float value); +void Phaser_SetOutputLevel(uint8_t unused __attribute__((unused)), float value); +void Phaser_SetLength(uint8_t unused __attribute__((unused)), float value); +void Phaser_SetG(uint8_t unused __attribute__((unused)), float value); +void Phaser_SetAuto(uint8_t unused __attribute__((unused)), float value); +void Phaser_SetDepth(uint8_t unused __attribute__((unused)), uint8_t value_u8); +void Phaser_SetG(uint8_t unused __attribute__((unused)), uint8_t value_u8); + + +void Phaser_ProcessHQ(const float *in, const float *lfo_in, float *out, int buffLen, float phaserDepth, struct allpass_s *ap0); + + +#endif /* SRC_ML_PHASER_H_ */ + diff --git a/src/ml_pitch_shifter.cpp b/src/ml_pitch_shifter.cpp new file mode 100644 index 0000000..2b8925c --- /dev/null +++ b/src/ml_pitch_shifter.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_pitch_shifter.cpp + * @author Marcel Licence + * @date 01.09.2023 + * + * @brief PitchShifter implementation + * + * @see little demo: https://youtu.be/hqK_U22Jha8 + */ + + +#ifdef __CDT_PARSER__ +#include +#endif + + +#include + +#include + + +#include +#include + + +ML_PitchShifter::ML_PitchShifter(float sample_rate) +{ + this->sample_rate = sample_rate; + inCnt = 0; + outCnt = 0; + speed = 1; + depth = 1.0f; + wetV = 1.0f; + dryV = 0.0f; + memset(buffer, 0, sizeof(buffer)); + feedback = 0.125f; +} + + +uint32_t minDistance(int32_t pointerA, int32_t pointerB) +{ + uint32_t distance1 = abs(pointerA - pointerB); + uint32_t distance2 = PITCH_SHIFTER_BUFFER_SIZE - distance1; + return (distance1 < distance2) ? distance1 : distance2; +} + + +void ML_PitchShifter::ProcessHQ(const float *in, float *out, uint32_t count) +{ + ML_PitchShifter_ProcessHQ(in, buffer, out, count, speed, inCnt, outCnt, wetV, dryV, feedback); +} + +void ML_PitchShifter::Process(const float *in, float *out, uint32_t count) +{ + const float speed_c = speed; + for (uint32_t n = 0; n < count; n++) + { + float outCnt2 = outCnt + (PITCH_SHIFTER_BUFFER_SIZE / 2); + if (outCnt2 >= PITCH_SHIFTER_BUFFER_SIZE) + { + outCnt2 -= PITCH_SHIFTER_BUFFER_SIZE; + } + + buffer[inCnt] = in[n]; + uint32_t outU = floor(outCnt); + uint32_t outU2 = floor(outCnt2); + uint32_t diffU = minDistance(inCnt, outU); + float diff = diffU; + diff *= 1.0f / (PITCH_SHIFTER_BUFFER_SIZE / 2); + float diffI = 1.0f - diff; + out[n] = (diff * buffer[outU] + diffI * buffer[outU2]) * wetV + in[n] * dryV; + + buffer[inCnt] += feedback * out[n]; + + inCnt++; + if (inCnt >= PITCH_SHIFTER_BUFFER_SIZE) + { + inCnt -= PITCH_SHIFTER_BUFFER_SIZE; + } + + outCnt += speed_c; + outCnt++; + if (outCnt >= PITCH_SHIFTER_BUFFER_SIZE) + { + outCnt -= PITCH_SHIFTER_BUFFER_SIZE; + } + if (outCnt < 0) + { + outCnt += PITCH_SHIFTER_BUFFER_SIZE; + } + } +} + +void ML_PitchShifter::setDepth(float depth) +{ + this->depth = depth; + Status_ValueChangedFloat("PitchShifter", "Depth", depth); +} + +void ML_PitchShifter::setSpeed(float speed) +{ + this->speed = speed; + Status_ValueChangedFloat("PitchShifter", "Speed", speed); +} + +void ML_PitchShifter::setFeedback(float feedback) +{ + this->feedback = feedback; + Status_ValueChangedFloat("PitchShifter", "Feedback", feedback); +} + +/** + * Works like a cross fader. + * Mix = 0.0 : dry = 1.0f, wet = 0.0f + * Mix = 0.5 : dry = 1.0f, wet = 1.0f + * Mix = 1.0 : dry = 0.0f, wet = 1.0f + * + * @param mix + * @param dry + * @param wet + */ +void MixToDryWet(float mix, float *dry, float *wet) +{ + if (mix >= 0.5f) + { + *dry = (1.0f - mix) * 2.0f; + } + else + { + *dry = 1.0f; + } + + if (mix <= 0.5f) + { + *wet = (mix) * 2.0f; + } + else + { + *wet = 1.0f; + } +} + +void ML_PitchShifter::setMix(float mix) +{ + MixToDryWet(mix, &dryV, &wetV); + Status_ValueChangedFloat("PitchShifter", "Mix", mix); +} + diff --git a/src/ml_pitch_shifter.h b/src/ml_pitch_shifter.h new file mode 100644 index 0000000..bb877b6 --- /dev/null +++ b/src/ml_pitch_shifter.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_pitch_shifter.h + * @author Marcel Licence + * @date 01.09.2023 + * + * @brief PitchShifter declarations + * + * @see little demo: https://youtu.be/hqK_U22Jha8 + */ + + +#ifndef SRC_ML_PITCH_SHIFTER_H_ +#define SRC_ML_PITCH_SHIFTER_H_ + + +#include +#include + + +#include +#include + + +#define PITCH_SHIFTER_BUFFER_SIZE (1024*8) + + +class ML_PitchShifter +{ +public: + ML_PitchShifter(float sample_rate); + ~ML_PitchShifter() {}; + void Process(const float *in, float *out, uint32_t count); + void ProcessHQ(const float *in, float *out, uint32_t count); + void setDepth(float depth); + void setSpeed(float speed); + void setMix(float mix); + void setFeedback(float feedback); + +private: + float sample_rate; + float depth; + float buffer[PITCH_SHIFTER_BUFFER_SIZE]; + int32_t inCnt; + float outCnt; + float speed; + float dryV; + float wetV; + float feedback; +}; + + +void ML_PitchShifter_ProcessHQ(const float *in, float *buffer, float *out, uint32_t count, float speed, int32_t &inCnt, float &outCnt, float wetV, float dryV, float feedback); + + +#endif /* SRC_ML_PITCH_SHIFTER_H_ */ + diff --git a/src/ml_utils.cpp b/src/ml_utils.cpp new file mode 100644 index 0000000..e40d181 --- /dev/null +++ b/src/ml_utils.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_utils.cpp + * @author Marcel Licence + * @date 02.09.2023 + * + * @brief Some useful functions + */ + + +#ifdef __CDT_PARSER__ +#include +#endif + + +#include + +#include +#include + + +float maxAbsValueFromSampleBuffer(float *samples, uint16_t sample_count) +{ + float absMax = 0; + + /* find max value */ + for (int n = 0; n < sample_count; n++) + { + absMax = samples[n] > absMax ? samples[n] : absMax; + absMax = -samples[n] > absMax ? -samples[n] : absMax; + } + + return absMax; +} + +float maxValueFromSampleBuffer(float *samples, uint16_t sample_count) +{ + float maxValue = 0; + + /* find max value */ + for (int n = 0; n < sample_count; n++) + { + maxValue = samples[n] > maxValue ? samples[n] : maxValue; + } + + return maxValue; +} + +float minValueFromSampleBuffer(float *samples, uint16_t sample_count) +{ + float minValue = 0; + + /* find max value */ + for (int n = 0; n < sample_count; n++) + { + minValue = samples[n] < minValue ? samples[n] : minValue; + } + + return minValue; +} + +/* convert a value between 0..1 to log scale with min at 16 bit 1/0 */ +float log16bit(float f) +{ + if (f < (1.0 / 65535.0f)) + { + return 0; + } + else if (f > 1.0f) + { + return 1.0f; + } + else + { + float range = -((log(1) / log(10) * 20) - (log(0xFFFF) / log(10) * 20)); + f *= 65535.0f; + f = (range + log(f / 65535.0f) / log(10) * 20) / range; + return f; + } +} + +float floatFromU7(uint8_t value) +{ + float f = value; + f *= (1.0f / 127.0f); + return f; +} + +float log2fromU7(uint8_t value, float minExp, float maxExp) +{ + float f = floatFromU7(value); + float minV = minExp; + float maxV = maxExp; + f *= maxV - minV; + f += minV; + return pow(2, f); +} + +float log10fromU7(uint8_t value, float minExp, float maxExp) +{ + float f = floatFromU7(value); + float minV = minExp; + float maxV = maxExp; + f *= maxV - minV; + f += minV; + return pow(10, f); +} + diff --git a/src/ml_utils.h b/src/ml_utils.h new file mode 100644 index 0000000..eb7f73d --- /dev/null +++ b/src/ml_utils.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_utils.h + * @author Marcel Licence + * @date 02.09.2023 + * + * @brief Some useful functions + */ + + +#ifndef SRC_ML_UTILS_H_ +#define SRC_ML_UTILS_H_ + + +#include + + +float maxAbsValueFromSampleBuffer(float *samples, uint16_t sample_count); +float maxValueFromSampleBuffer(float *samples, uint16_t sample_count); +float minValueFromSampleBuffer(float *samples, uint16_t sample_count); + +float log16bit(float f); +float floatFromU7(uint8_t value); +float log2fromU7(uint8_t value, float minExp, float maxExp); +float log10fromU7(uint8_t value, float minExp, float maxExp); + + +#include + + +static inline +void mul(const float *in, float gain, float *out, uint32_t len) +{ + for (uint32_t n = 0; n < len; n++) + { + out[n] = gain * in[n]; + } +} + +static inline +void mul(const Q1_14 *in, float gain, Q1_14 *out, uint32_t len) +{ + for (uint32_t n = 0; n < len; n++) + { + float val = (float)in[n].s16 * gain; + out[n].s16 = val; + } +} + +static inline +void mixStereoToMono(const Q1_14 *in_l, const Q1_14 *in_r, float *mono_out, uint32_t len) +{ + for (uint32_t n = 0; n < len; n++) + { + mono_out[n] = (in_l[n].s16 * (0.5f / 16384.0f)) + (in_r[n].s16 * (0.5f / 16384.0f)); + } +} + + +#endif /* SRC_ML_UTILS_H_ */ + diff --git a/src/ml_vibrato.cpp b/src/ml_vibrato.cpp new file mode 100644 index 0000000..e4d5f9a --- /dev/null +++ b/src/ml_vibrato.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_vibrato.cpp + * @author Marcel Licence + * @date 01.09.2023 + * + * @brief Vibrato implementation + * + * @see little demo: https://youtu.be/hqK_U22Jha8 + */ + + +#ifdef __CDT_PARSER__ +#include +#endif + + +#include + +#include + +#include + + +ML_Vibrato::ML_Vibrato(float sample_rate) +{ + this->sample_rate = sample_rate; + inCnt = 0; + depth = 1.0f; + depthInv = 0.0f; + mod_multiplier = 0.5f * (VIBRATO_BUFFER_SIZE - 2); + mod_multiplier_curr = mod_multiplier; + memset(buffer, 0, sizeof(buffer)); +} + +void ML_Vibrato::ModMultiplierUpdate() +{ + if (mod_multiplier_curr > mod_multiplier) + { + mod_multiplier_curr --; + } + if (mod_multiplier_curr < mod_multiplier) + { + mod_multiplier_curr ++; + } +} + +void ML_Vibrato::Process(const float *in, const float *mod_in, float *out, uint32_t count) +{ + ModMultiplierUpdate(); + for (uint32_t n = 0; n < count; n++) + { + float mod = (1.0f + mod_in[n]) * mod_multiplier_curr; + int outCnt = inCnt - mod; + if (outCnt < 0) + { + outCnt += VIBRATO_BUFFER_SIZE; + } + + buffer[inCnt] = in[n]; + out[n] = depth * buffer[outCnt] + depthInv * in[n]; + + inCnt++; + if (inCnt >= VIBRATO_BUFFER_SIZE) + { + inCnt -= VIBRATO_BUFFER_SIZE; + } + } +} + +void ML_Vibrato::ProcessHQ(const float *in, const float *mod_in, float *out, uint32_t count) +{ + ModMultiplierUpdate(); + ML_Vibrato_ProcessHQ(in, mod_in, out, count, mod_multiplier_curr, inCnt, buffer, depth, depthInv); +} + +void ML_Vibrato::setDepth(float depth) +{ + this->depth = depth; + this->depthInv = 1.0f - depth; + Status_ValueChangedFloat("Vibrato", "Depth", depth); +} + +void ML_Vibrato::setIntensity(float intensity) +{ + mod_multiplier = 0.5f * (VIBRATO_BUFFER_SIZE - 2) * intensity; + Status_ValueChangedFloat("Vibrato", "Intensity", depth); +} + diff --git a/src/ml_vibrato.h b/src/ml_vibrato.h new file mode 100644 index 0000000..7177b66 --- /dev/null +++ b/src/ml_vibrato.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Marcel Licence + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 . + * + * Dieses Programm ist Freie Software: Sie können es unter den Bedingungen + * der GNU General Public License, wie von der Free Software Foundation, + * Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren + * veröffentlichten Version, weiter verteilen und/oder modifizieren. + * + * Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch + * OHNE JEDE GEWÄHR,; sogar ohne die implizite + * Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + * Siehe die GNU General Public License für weitere Einzelheiten. + * + * Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + * Programm erhalten haben. Wenn nicht, siehe . + */ + +/** + * @file ml_vibrato.h + * @author Marcel Licence + * @date 01.09.2023 + * + * @brief Vibrato declarations + * + * @see little demo: https://youtu.be/hqK_U22Jha8 + */ + + +#ifndef SRC_ML_VIBRATO_H_ +#define SRC_ML_VIBRATO_H_ + + +#include +#include + + +#include +#include + + +#define VIBRATO_BUFFER_SIZE 1024 + + +class ML_Vibrato +{ +public: + ML_Vibrato(float sample_rate); + ~ML_Vibrato() {}; + void Process(const float *in, const float *mod_in, float *out, uint32_t count); + void ProcessHQ(const float *in, const float *mod_in, float *out, uint32_t count); + void setDepth(float depth); + void setIntensity(float intensity); + +private: + void ModMultiplierUpdate(); + + float sample_rate; + float depth; + float depthInv; + float buffer[VIBRATO_BUFFER_SIZE]; + float mod_multiplier; + float mod_multiplier_curr; + int32_t inCnt; +}; + + +void ML_Vibrato_ProcessHQ(const float *in, const float *mod_in, float *out, uint32_t count, float mod_multiplier, int32_t &inCnt, float *buffer, float depth, float depthInv); + + +#endif /* SRC_ML_VIBRATO_H_ */ +