Add fake tides

This commit is contained in:
Jan-Henrik 2020-03-15 01:08:06 +01:00
parent a7b31cb9e7
commit d7294bbc12
7 changed files with 581 additions and 4 deletions

View file

@ -0,0 +1,81 @@
// Copyright 2015 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Quantize a float in [0, 1] to an integer in [0, num_steps[. Apply hysteresis
// to prevent jumps near the decision boundary.
#ifndef STMLIB_DSP_HYSTERESIS_QUANTIZER_H_
#define STMLIB_DSP_HYSTERESIS_QUANTIZER_H_
#include <avrlib/avrlib.h>
namespace pure_adsr {
class HysteresisQuantizer {
public:
HysteresisQuantizer() { }
~HysteresisQuantizer() { }
void Init() {
quantized_value_ = 0;
}
inline int Process(float value, int num_steps) {
return Process(value, num_steps, 0.25f);
}
inline int Process(float value, int num_steps, float hysteresis) {
return Process(0, value, num_steps, hysteresis);
}
inline int Process(int base, float value, int num_steps, float hysteresis) {
value *= static_cast<float>(num_steps - 1);
value += static_cast<float>(base);
float hysteresis_feedback = value > static_cast<float>(quantized_value_)
? -hysteresis
: hysteresis;
int q = static_cast<int>(value + hysteresis_feedback + 0.5f);
if(q < 0) q = 0;
if(q > num_steps - 1) q = num_steps - 1;
quantized_value_ = q;
return q;
}
template<typename T>
const T& Lookup(const T* array, float value, int num_steps) {
return array[Process(value, num_steps)];
}
private:
int quantized_value_;
DISALLOW_COPY_AND_ASSIGN(HysteresisQuantizer);
};
} // namespace stmlib
#endif // STMLIB_DSP_HYSTERESIS_QUANTIZER_H_

View file

@ -13,6 +13,7 @@
#include "pure_adsr/io_buffer.h"
#include "pure_adsr/mini_sequencer.h"
#include "pure_adsr/multistage_envelope.h"
#include "pure_adsr/tides.h"
using namespace avrlib;
using namespace pure_adsr;
@ -20,7 +21,7 @@ using namespace pure_adsr;
typedef enum {
MODE_ADSR,
MODE_SEQ,
MODE_LFO
MODE_TIDES
} Mode;
typedef SpiMaster<Gpio<PortB, 2>, MSB_FIRST, 2> dac1Spi;
@ -38,6 +39,7 @@ typedef AdcInputScanner AnalogInputs;
MultistageEnvelope envelope;
MiniSequencer sequencer;
Tides tides;
IOBuffer io_buffer;
int16_t output_buffer[kBlockSize];
@ -55,6 +57,7 @@ void Process(IOBuffer::Block* block, size_t size)
pot_values[i] = read_value;
envelope.Configure(pot_values, CONTROL_MODE_FULL);
sequencer.Configure(pot_values, CONTROL_MODE_FULL);
tides.Configure(pot_values, CONTROL_MODE_FULL);
}
}
@ -66,7 +69,9 @@ void Process(IOBuffer::Block* block, size_t size)
break;
case MODE_SEQ:
sequencer.Process(block->input[i], output_buffer, size);
default:
break;
case MODE_TIDES:
tides.Process(block->input[i], output_buffer, size);
break;
}
@ -101,6 +106,7 @@ void Init()
pots[4 - (i + 1)] = AnalogInputs::Read8(i) << 8;
envelope.Configure(pots, CONTROL_MODE_FULL);
sequencer.Configure(pots, CONTROL_MODE_FULL);
tides.Configure(pots, CONTROL_MODE_FULL);
Timer<2>::Start();
}
@ -128,7 +134,7 @@ TIMER_0_TICK
mode = MODE_SEQ;
break;
case 1:
mode = MODE_LFO;
mode = MODE_TIDES;
break;
case 2:
mode = MODE_ADSR;

View file

@ -215,10 +215,46 @@ const prog_uint32_t lut_res_env_increments[] PROGMEM = {
253893, 249198, 244605, 240110, 235713, 231409, 227198, 223077,
219043,
};
const prog_uint32_t lut_res_env_increments_slow[] PROGMEM = {
35046933, 33184343, 31436989, 29796754, 28256163, 26808323, 25446875, 24165949,
22960121, 21824376, 20754076, 19744924, 18792940, 17894435, 17045985, 16244414,
15486770, 14770309, 14092480, 13450909, 12843386, 12267850, 11722382, 11205191,
10714605, 10249065, 9807112, 9387383, 8988604, 8609583, 8249203, 7906418,
7580249, 7269776, 6974139, 6692528, 6424184, 6168395, 5924489, 5691837,
5469847, 5257961, 5055654, 4862432, 4677829, 4501406, 4332750, 4171471,
4017200, 3869589, 3728311, 3593055, 3463528, 3339453, 3220568, 3106625,
2997389, 2892639, 2792164, 2695765, 2603253, 2514449, 2429184, 2347295,
2268632, 2193047, 2120405, 2050573, 1983428, 1918852, 1856732, 1796962,
1739439, 1684069, 1630758, 1579418, 1529968, 1482327, 1436419, 1392173,
1349520, 1308394, 1268733, 1230477, 1193569, 1157955, 1123584, 1090404,
1058371, 1027437, 997560, 968699, 940814, 913868, 887824, 862647,
838306, 814767, 792001, 769979, 748673, 728057, 708104, 688790,
670093, 651989, 634458, 617477, 601028, 585092, 569649, 554684,
540178, 526116, 512482, 499262, 486440, 474003, 461939, 450233,
438875, 427852, 417153, 406766, 396683, 386891, 377383, 368148,
359177, 350462, 341994, 333766, 325770, 317997, 310442, 303096,
295954, 289009, 282255, 275685, 269295, 263077, 257028, 251142,
245413, 239837, 234409, 229126, 223981, 218972, 214094, 209343,
204715, 200207, 195816, 191537, 187368, 183304, 179344, 175484,
171722, 168054, 164477, 160990, 157589, 154272, 151037, 147882,
144803, 141800, 138869, 136010, 133219, 130496, 127837, 125242,
122709, 120236, 117821, 115462, 113160, 110911, 108714, 106568,
104472, 102424, 100423, 98468, 96557, 94690, 92865, 91081,
89338, 87633, 85967, 84338, 82744, 81187, 79663, 78173,
76716, 75290, 73895, 72531, 71196, 69890, 68612, 67362,
66138, 64940, 63767, 62620, 61496, 60396, 59320, 58265,
57233, 56222, 55232, 54262, 53313, 52382, 51471, 50579,
49704, 48847, 48008, 47185, 46379, 45589, 44815, 44056,
43313, 42584, 41869, 41168, 40481, 39808, 39148, 38500,
37865, 37243, 36632, 36033, 35446, 34869, 34304, 33750,
33206, 32672, 32148, 31634, 31130, 30635, 30150, 29673,
29205,
};
const prog_uint32_t* const lookup_table_32_table[] = {
lut_res_env_increments,
lut_res_env_increments_slow,
};

View file

@ -44,6 +44,7 @@ extern const prog_uint16_t lut_res_env_expo[] PROGMEM;
extern const prog_uint16_t lut_res_env_quartic[] PROGMEM;
extern const prog_uint16_t lut_res_raised_cosine[] PROGMEM;
extern const prog_uint32_t lut_res_env_increments[] PROGMEM;
extern const prog_uint32_t lut_res_env_increments_slow[] PROGMEM;
#define STR_RES_DUMMY 0 // dummy
#define LUT_RES_ENV_LINEAR 0
#define LUT_RES_ENV_LINEAR_SIZE 257
@ -55,6 +56,8 @@ extern const prog_uint32_t lut_res_env_increments[] PROGMEM;
#define LUT_RES_RAISED_COSINE_SIZE 257
#define LUT_RES_ENV_INCREMENTS 0
#define LUT_RES_ENV_INCREMENTS_SIZE 257
#define LUT_RES_ENV_INCREMENTS_SLOW 1
#define LUT_RES_ENV_INCREMENTS_SLOW_SIZE 257
} // namespace pure_adsr

View file

@ -61,6 +61,20 @@ lookup_tables_32.append(
('env_increments', values)
)
max_time = 60.0 # seconds
min_time = 0.05
gamma = 0.175
min_increment = excursion / (max_time * sample_rate)
max_increment = excursion / (min_time * sample_rate)
rates = numpy.linspace(numpy.power(max_increment, -gamma),
numpy.power(min_increment, -gamma), num_values)
values = numpy.power(rates, -1/gamma).astype(int)
lookup_tables_32.append(
('env_increments_slow', values)
)
"""----------------------------------------------------------------------------
Envelope curves
-----------------------------------------------------------------------------"""
@ -69,7 +83,6 @@ env_linear = numpy.arange(0, 257.0) / 256.0
env_linear[-1] = env_linear[-2]
env_quartic = env_linear ** 3.32
env_expo = 1.0 - numpy.exp(-4 * env_linear)
lookup_tables.append(('env_linear', env_linear / env_linear.max() * 65535.0))
lookup_tables.append(('env_expo', env_expo / env_expo.max() * 65535.0))
lookup_tables.append(

89
pure_adsr/tides.cc Normal file
View file

@ -0,0 +1,89 @@
// Copyright 2013 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Multistage envelope.
#include "pure_adsr/tides.h"
#include "pure_adsr/resources.h"
namespace pure_adsr {
void Tides::Init()
{
set_adsr(0, 8192, 16384, 32767);
segment_ = num_segments_;
phase_ = 0;
phase_increment_ = 0;
start_value_ = 0;
value_ = 0;
hard_reset_ = false;
quant.Init();
}
void Tides::Process(
const GateFlags* gate_flags, int16_t* out, size_t size)
{
while (size--) {
GateFlags gate_flag = *gate_flags++;
if (gate_flag & GATE_FLAG_RISING) {
start_value_ = (segment_ == num_segments_ || hard_reset_)
? level_[0]
: value_;
segment_ = 0;
phase_ = 0;
} else if (gate_flag & GATE_FLAG_FALLING && sustain_point_) {
start_value_ = value_;
segment_ = sustain_point_;
phase_ = 0;
} else if (phase_ < phase_increment_) {
start_value_ = level_[segment_ + 1];
++segment_;
phase_ = 0;
if (segment_ == loop_end_) {
segment_ = loop_start_;
}
}
bool done = segment_ == num_segments_;
bool sustained = sustain_point_ && segment_ == sustain_point_ && gate_flag & GATE_FLAG_HIGH;
phase_increment_ = sustained || done ? 0 : pgm_read_dword(lut_res_env_increments_slow + (time_[segment_] >> 8));
int32_t a = start_value_;
int32_t b = level_[segment_ + 1];
uint32_t x = pgm_read_word(lookup_table_table[LUT_RES_ENV_LINEAR + shape_[segment_]] + (phase_ >> 24));
uint32_t y = pgm_read_word(lookup_table_table[LUT_RES_ENV_LINEAR + shape_[segment_]] + (phase_ >> 24) + 1);
uint16_t t = x + ((y - x) * static_cast<uint32_t>((phase_ >> 8) & 0xffff) >> 16);
value_ = a + ((b - a) * (t >> 1) >> 15);
phase_ += phase_increment_;
*out++ = value_;
}
}
} // namespace peaks

349
pure_adsr/tides.h Normal file
View file

@ -0,0 +1,349 @@
// Copyright 2013 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Multistage envelope.
#ifndef PEAKS_MODULATIONS_TIDES_H_
#define PEAKS_MODULATIONS_TIDES_H_
#include "pure_adsr/gate_processor.h"
#include "pure_adsr/hysteresis_quantizer.h"
#include "pure_adsr/multistage_envelope.h"
#include <avrlib/avrlib.h>
#include <stddef.h>
namespace pure_adsr {
class Tides {
public:
Tides() {}
~Tides() {}
void Init();
void Process(const GateFlags* gate_flags, int16_t* out, size_t size);
void Configure(uint16_t* parameter, ControlMode control_mode)
{
set_ar_loop(parameter[0], parameter[1]);
uint8_t waveform_mode = quant.Process(parameter[2] / 65536.0f, 5);
switch (waveform_mode) {
case 0:
shape_[0] = ENV_SHAPE_QUARTIC;
shape_[1] = ENV_SHAPE_EXPONENTIAL;
break;
case 1:
shape_[0] = ENV_SHAPE_QUARTIC;
shape_[1] = ENV_SHAPE_QUARTIC;
break;
case 2:
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
break;
case 3:
shape_[0] = ENV_SHAPE_EXPONENTIAL;
shape_[1] = ENV_SHAPE_EXPONENTIAL;
break;
case 4:
shape_[0] = ENV_SHAPE_EXPONENTIAL;
shape_[1] = ENV_SHAPE_QUARTIC;
break;
}
if (segment_ > num_segments_) {
segment_ = 0;
phase_ = 0;
value_ = 0;
}
}
inline void set_time(uint16_t segment, uint16_t time)
{
time_[segment] = time;
}
inline void set_level(uint16_t segment, int16_t level)
{
level_[segment] = level;
}
inline void set_num_segments(uint16_t num_segments)
{
num_segments_ = num_segments;
}
inline void set_sustain_point(uint16_t sustain_point)
{
sustain_point_ = sustain_point;
}
inline void set_adsr(
uint16_t attack,
uint16_t decay,
uint16_t sustain,
uint16_t release)
{
num_segments_ = 3;
sustain_point_ = 2;
level_[0] = 0;
level_[1] = 32767;
level_[2] = sustain;
level_[3] = 0;
time_[0] = attack;
time_[1] = decay;
time_[2] = release;
shape_[0] = ENV_SHAPE_QUARTIC;
shape_[1] = ENV_SHAPE_EXPONENTIAL;
shape_[2] = ENV_SHAPE_EXPONENTIAL;
loop_start_ = loop_end_ = 0;
}
inline void set_ad(uint16_t attack, uint16_t decay)
{
num_segments_ = 2;
sustain_point_ = 0;
level_[0] = 0;
level_[1] = 32767;
level_[2] = 0;
time_[0] = attack;
time_[1] = decay;
shape_[0] = ENV_SHAPE_EXPONENTIAL;
shape_[1] = ENV_SHAPE_EXPONENTIAL;
loop_start_ = loop_end_ = 0;
}
inline void set_adr(
uint16_t attack,
uint16_t decay,
uint16_t sustain,
uint16_t release)
{
num_segments_ = 3;
sustain_point_ = 0;
level_[0] = 0;
level_[1] = 32767;
level_[2] = sustain;
level_[3] = 0;
time_[0] = attack;
time_[1] = decay;
time_[2] = release;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
shape_[2] = ENV_SHAPE_LINEAR;
loop_start_ = loop_end_ = 0;
}
inline void set_ar(uint16_t attack, uint16_t decay)
{
num_segments_ = 2;
sustain_point_ = 1;
level_[0] = 0;
level_[1] = 32767;
level_[2] = 0;
time_[0] = attack;
time_[1] = decay;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
loop_start_ = loop_end_ = 0;
}
inline void set_adsar(
uint16_t attack,
uint16_t decay,
uint16_t sustain,
uint16_t release)
{
num_segments_ = 4;
sustain_point_ = 2;
level_[0] = 0;
level_[1] = 32767;
level_[2] = sustain;
level_[3] = 32767;
level_[4] = 0;
time_[0] = attack;
time_[1] = decay;
time_[2] = attack;
time_[3] = release;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
shape_[2] = ENV_SHAPE_LINEAR;
shape_[3] = ENV_SHAPE_LINEAR;
loop_start_ = loop_end_ = 0;
}
inline void set_adar(
uint16_t attack,
uint16_t decay,
uint16_t sustain,
uint16_t release)
{
num_segments_ = 4;
sustain_point_ = 0;
level_[0] = 0;
level_[1] = 32767;
level_[2] = sustain;
level_[3] = 32767;
level_[4] = 0;
time_[0] = attack;
time_[1] = decay;
time_[2] = attack;
time_[3] = release;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
shape_[2] = ENV_SHAPE_LINEAR;
shape_[3] = ENV_SHAPE_LINEAR;
loop_start_ = loop_end_ = 0;
}
inline void set_ad_loop(uint16_t attack, uint16_t decay)
{
num_segments_ = 2;
sustain_point_ = 0;
level_[0] = 0;
level_[1] = 32767;
level_[2] = 0;
time_[0] = attack;
time_[1] = decay;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
loop_start_ = 0;
loop_end_ = 2;
}
inline void set_adr_loop(
uint16_t attack,
uint16_t decay,
uint16_t sustain,
uint16_t release)
{
num_segments_ = 3;
sustain_point_ = 0;
level_[0] = 0;
level_[1] = 32767;
level_[2] = sustain;
level_[3] = 0;
time_[0] = attack;
time_[1] = decay;
time_[2] = release;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
shape_[2] = ENV_SHAPE_LINEAR;
loop_start_ = 0;
loop_end_ = 3;
}
inline void set_adar_loop(
uint16_t attack,
uint16_t decay,
uint16_t sustain,
uint16_t release)
{
num_segments_ = 4;
sustain_point_ = 0;
level_[0] = 0;
level_[1] = 32767;
level_[2] = sustain;
level_[3] = 32767;
level_[4] = 0;
time_[0] = attack;
time_[1] = decay;
time_[2] = attack;
time_[3] = release;
shape_[0] = ENV_SHAPE_LINEAR;
shape_[1] = ENV_SHAPE_LINEAR;
shape_[2] = ENV_SHAPE_LINEAR;
shape_[3] = ENV_SHAPE_LINEAR;
loop_start_ = 0;
loop_end_ = 4;
}
inline void set_hard_reset(bool hard_reset)
{
hard_reset_ = hard_reset;
}
private:
int16_t level_[kMaxNumSegments];
uint16_t time_[kMaxNumSegments];
EnvelopeShape shape_[kMaxNumSegments];
int16_t segment_;
int16_t start_value_;
int16_t value_;
uint32_t phase_;
uint32_t phase_increment_;
uint16_t num_segments_;
uint16_t sustain_point_;
uint16_t loop_start_;
uint16_t loop_end_;
bool hard_reset_;
HysteresisQuantizer quant;
DISALLOW_COPY_AND_ASSIGN(Tides);
};
} // namespace peaks
#endif // PEAKS_MODULATIONS_MULTISTAGE_ENVELOPE_H_