This commit is contained in:
Chris
2023-08-12 09:35:16 +02:00
8 changed files with 283 additions and 84 deletions

View File

@@ -73,10 +73,10 @@ public:
} }
void processblock(t_sample** inputs, t_sample** outputs, int blockSize) void processblock(t_sample** inputs, t_sample** outputs, int blockSize)
{ {
double* in1 = inputs[0]; t_sample* in1 = inputs[0];
double* in2 = inputs[1]; t_sample* in2 = inputs[1];
double* out1 = outputs[0]; t_sample* out1 = outputs[0];
double* out2 = outputs[1]; t_sample* out2 = outputs[1];
int inFramesToProcess = blockSize; int inFramesToProcess = blockSize;
double overallscale = 1.0; double overallscale = 1.0;

View File

@@ -73,10 +73,10 @@ public:
} }
void processblock(t_sample** inputs, t_sample** outputs, int blockSize) void processblock(t_sample** inputs, t_sample** outputs, int blockSize)
{ {
double* in1 = inputs[0]; t_sample* in1 = inputs[0];
double* in2 = inputs[1]; t_sample* in2 = inputs[1];
double* out1 = outputs[0]; t_sample* out1 = outputs[0];
double* out2 = outputs[1]; t_sample* out2 = outputs[1];
int inFramesToProcess = blockSize; int inFramesToProcess = blockSize;
double overallscale = 1.0; double overallscale = 1.0;

View File

@@ -73,10 +73,10 @@ public:
} }
void processblock(t_sample** inputs, t_sample** outputs, int blockSize) void processblock(t_sample** inputs, t_sample** outputs, int blockSize)
{ {
double* in1 = inputs[0]; t_sample* in1 = inputs[0];
double* in2 = inputs[1]; t_sample* in2 = inputs[1];
double* out1 = outputs[0]; t_sample* out1 = outputs[0];
double* out2 = outputs[1]; t_sample* out2 = outputs[1];
int inFramesToProcess = blockSize; int inFramesToProcess = blockSize;
double overallscale = 1.0; double overallscale = 1.0;

View File

@@ -16,7 +16,7 @@ enum filter_types {
template <typename t_sample> template <typename t_sample>
class ysvf { class ysvf {
public: public:
ysvf(double _samplerate) ysvf(double _samplerate = 44100)
: lowpass { _samplerate } : lowpass { _samplerate }
, highpass { _samplerate } , highpass { _samplerate }
, bandpass { _samplerate } , bandpass { _samplerate }
@@ -96,10 +96,10 @@ public:
private: private:
filter_types filter_type; filter_types filter_type;
ylowpass lowpass; ylowpass<t_sample> lowpass;
yhighpass highpass; yhighpass<t_sample> highpass;
ybandpass bandpass; ybandpass<t_sample> bandpass;
ynotch notch; ynotch<t_sample> notch;
double clamp(double& value, double min, double max) { double clamp(double& value, double min, double max) {
if (value < min) { if (value < min) {

View File

@@ -17,54 +17,45 @@ enum env_state {
class tx_envelope { class tx_envelope {
public: public:
env_state state; env_state state = idle;
float attack1_rate; float attack1_rate = 0;
float attack1_level; float attack1_level = 0;
float attack2_rate; float attack2_rate = 0;
float hold_rate; float hold_rate = 0;
float decay1_rate; float decay1_rate = 0;
float decay1_level; float decay1_level = 0;
float decay2_rate; float decay2_rate = 0;
float sustain_level; float sustain_level = 0;
float release1_rate; float release1_rate = 0;
float release1_level; float release1_level = 0;
float release2_rate; float release2_rate = 0;
tx_envelope() tx_envelope(bool _retrigger = false)
: samplerate { 44100. } : retrigger { _retrigger }
, attack1_rate { 0 }
, attack1_level { 0 }
, attack2_rate { 0 }
, hold_rate { 0 }
, decay1_rate { 0 }
, decay1_level { 0 }
, decay2_rate { 0 }
, sustain_level { 0 }
, release1_rate { 0 }
, release1_level { 0 }
, release2_rate { 0 }
, level { 0.f }
, phase { 0 }
, state { idle }
, start_level { 0.f }
, h1 { 0. }
, h2 { 0. }
, h3 { 0. }
{ {
} }
float process_sample(bool gate, bool trigger) { float process_sample(bool gate, bool trigger) {
int attack_mid_x1 = ms_to_samples(attack1_rate); return process_sample<float>(gate, trigger, 0, 0);
int attack_mid_x2 = ms_to_samples(attack2_rate); }
int hold_samp = ms_to_samples(hold_rate);
int decay_mid_x1 = ms_to_samples(decay1_rate); template <typename t_sample>
int decay_mid_x2 = ms_to_samples(decay2_rate); float process_sample(bool gate, bool trigger, t_sample _attack_mod, t_sample _decay_mod) {
int release_mid_x1 = ms_to_samples(release1_rate);
int release_mid_x2 = ms_to_samples(release2_rate); size_t attack_mid_x1 = ms_to_samples(attack1_rate + (float)_attack_mod);
size_t attack_mid_x2 = ms_to_samples(attack2_rate + (float)_attack_mod);
size_t hold_samp = ms_to_samples(hold_rate);
size_t decay_mid_x1 = ms_to_samples(decay1_rate + (float)_decay_mod);
size_t decay_mid_x2 = ms_to_samples(decay2_rate + (float)_decay_mod);
size_t release_mid_x1 = ms_to_samples(release1_rate + (float)_decay_mod);
size_t release_mid_x2 = ms_to_samples(release2_rate + (float)_decay_mod);
// if note on is triggered, transition to attack phase // if note on is triggered, transition to attack phase
if (trigger) { if (trigger) {
if (retrigger)
start_level = 0.f;
else
start_level = level; start_level = level;
phase = 0; phase = 0;
state = attack1; state = attack1;
@@ -73,7 +64,7 @@ public:
if (state == attack1) { if (state == attack1) {
// while in attack phase // while in attack phase
if (phase < attack_mid_x1) { if (phase < attack_mid_x1) {
level = lerp(0, start_level, attack_mid_x1, attack1_level, phase); level = lerp(0, start_level, (float)attack_mid_x1, attack1_level, (float)phase);
phase += 1; phase += 1;
} }
// reset phase if parameter was changed // reset phase if parameter was changed
@@ -90,7 +81,7 @@ public:
if (state == attack2) { if (state == attack2) {
// while in attack phase // while in attack phase
if (phase < attack_mid_x2) { if (phase < attack_mid_x2) {
level = lerp(0, attack1_level, attack_mid_x2, 1, phase); level = lerp(0, attack1_level, (float)attack_mid_x2, 1, (float)phase);
phase += 1; phase += 1;
} }
// reset phase if parameter was changed // reset phase if parameter was changed
@@ -121,7 +112,7 @@ public:
if (state == decay1) { if (state == decay1) {
// while in decay phase // while in decay phase
if (phase < decay_mid_x1) { if (phase < decay_mid_x1) {
level = lerp(0, 1, decay_mid_x1, decay1_level, phase); level = lerp(0, 1, (float)decay_mid_x1, decay1_level, (float)phase);
phase += 1; phase += 1;
} }
// reset phase if parameter was changed // reset phase if parameter was changed
@@ -138,7 +129,7 @@ public:
if (state == decay2) { if (state == decay2) {
// while in decay phase // while in decay phase
if (phase < decay_mid_x2) { if (phase < decay_mid_x2) {
level = lerp(0, decay1_level, decay_mid_x2, sustain_level, phase); level = lerp(0, decay1_level, (float)decay_mid_x2, sustain_level, (float)phase);
phase += 1; phase += 1;
} }
// reset phase if parameter was changed // reset phase if parameter was changed
@@ -161,7 +152,7 @@ public:
if (state == release1) { if (state == release1) {
// while in release phase // while in release phase
if (phase < release_mid_x1) { if (phase < release_mid_x1) {
level = lerp(0, sustain_level, release_mid_x1, release1_level, phase); level = lerp(0, sustain_level, (float)release_mid_x1, release1_level, (float)phase);
phase += 1; phase += 1;
} }
// reset phase if parameter was changed // reset phase if parameter was changed
@@ -178,7 +169,7 @@ public:
if (state == release2) { if (state == release2) {
// while in release phase // while in release phase
if (phase < release_mid_x2) { if (phase < release_mid_x2) {
level = lerp(0, release1_level, release_mid_x2, 0, phase); level = lerp(0, release1_level, (float)release_mid_x2, 0, (float)phase);
phase += 1; phase += 1;
} }
// reset phase if parameter was changed // reset phase if parameter was changed
@@ -202,37 +193,41 @@ public:
this->samplerate = sampleRate; this->samplerate = sampleRate;
} }
// returns the x/y coordinates of the envelope points as a list for graphical representation. // converts the x/y coordinates of the envelope points as a list for graphical representation.
std::array<float, 18> calc_coordinates() { std::array<float, 18> calc_coordinates(float _max_attack, float _max_decay, float _max_release) {
auto scale = [](float _value, float _max) {
return powf(_value / _max, 0.25) * _max;
};
float a_x = 0; float a_x = 0;
float a_y = 0; float a_y = 0;
float b_x = attack1_rate; float b_x = scale(attack1_rate, _max_attack / 2);
float b_y = attack1_level; float b_y = attack1_level;
float c_x = b_x + attack2_rate; float c_x = b_x + scale(attack2_rate, _max_attack / 2);
float c_y = 1; float c_y = 1;
float d_x = c_x + hold_rate; float d_x = c_x + hold_rate;
float d_y = 1; float d_y = 1;
float e_x = d_x + decay1_rate; float e_x = d_x + scale(decay1_rate, _max_decay / 2);
float e_y = decay1_level; float e_y = decay1_level;
float f_x = e_x + decay2_rate; float f_x = e_x + scale(decay2_rate, _max_decay / 2);
float f_y = sustain_level; float f_y = sustain_level;
float g_x = f_x + 125; float g_x = _max_attack + _max_decay;
float g_y = sustain_level; float g_y = sustain_level;
float h_x = g_x + release1_rate; float h_x = g_x + scale(release1_rate, _max_decay / 2);
float h_y = release1_level; float h_y = release1_level;
float i_x = h_x + release2_rate; float i_x = h_x + scale(release2_rate, _max_decay / 2);
float i_y = 0; float i_y = 0;
float total = i_x; float total = _max_attack + _max_decay + _max_release;
return { return {
a_x, a_x,
@@ -257,13 +252,14 @@ public:
} }
private: private:
double samplerate; double samplerate = 44100.;
int phase; size_t phase = 0;
float level; float level = 0.f;
float start_level; float start_level = 0.f;
float h1; float h1 = 0.f;
float h2; float h2 = 0.f;
float h3; float h3 = 0.f;
bool retrigger;
float lerp(float x1, float y1, float x2, float y2, float x) { return y1 + (((x - x1) * (y2 - y1)) / (x2 - x1)); } float lerp(float x1, float y1, float x2, float y2, float x) { return y1 + (((x - x1) * (y2 - y1)) / (x2 - x1)); }
@@ -275,6 +271,8 @@ private:
return (h1 + h2 + h3) / 3.f; return (h1 + h2 + h3) / 3.f;
} }
float ms_to_samples(float ms) { return ms * samplerate / 1000.f; } size_t ms_to_samples(float ms) {
return static_cast<size_t>(ms * samplerate / 1000.f);
}
}; };
} }

View File

@@ -11,7 +11,7 @@ public:
std::vector<t_voice> voices; std::vector<t_voice> voices;
voice_allocator() voice_allocator()
: voices(8, t_voice()) : voices(4, t_voice())
{ {
// checks whether template derives from ivoice // checks whether template derives from ivoice
typedef t_voice assert_at_compile_time[is_convertible<t_voice>::value ? 1 : -1]; typedef t_voice assert_at_compile_time[is_convertible<t_voice>::value ? 1 : -1];
@@ -92,6 +92,7 @@ public:
private: private:
std::vector<midi_event> input_queue; std::vector<midi_event> input_queue;
int index_to_steal = 0;
t_voice* get_free_voice(float frequency) t_voice* get_free_voice(float frequency)
{ {
@@ -118,7 +119,13 @@ private:
} }
if (free_voice == nullptr) { if (free_voice == nullptr) {
free_voice = &voices.at(0); free_voice = &voices.at(index_to_steal);
if (index_to_steal < voices.size() - 1) {
index_to_steal++;
} else {
index_to_steal = 0;
}
} }
return free_voice; return free_voice;

38
util/demo_noise.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#define _USE_MATH_DEFINES
#include <math.h>
#include <cstdlib>
namespace trnr {
template <typename t_sample>
class demo_noise {
public:
void set_samplerate(double _samplerate) {
samplerate = _samplerate;
}
void process_block(t_sample** samples, long sample_frames) {
for (int s = 0; s < sample_frames; s++) {
demo_counter++;
if (demo_counter == samplerate * 20) {
demo_counter = 0;
}
if (demo_counter > samplerate * 17) {
t_sample r1 = static_cast<t_sample>(rand()) / static_cast<t_sample>(RAND_MAX);
t_sample r2 = static_cast<t_sample>(rand()) / static_cast<t_sample>(RAND_MAX);
t_sample noise = static_cast<t_sample>(sqrt(-2.0 * log(r1)) * cos(2.0 * M_PI * r2));
samples[0][s] = noise / 10.0;
samples[1][s] = noise / 10.0;
}
}
}
private:
double samplerate = 44100;
int demo_counter = 0;
};
}

156
util/retro_buf.h Normal file
View File

@@ -0,0 +1,156 @@
#pragma once
#include "../filter/chebyshev.h"
#include "../companding/ulaw.h"
#include <iostream>
namespace trnr {
struct retro_buf_modulation {
double midi_note;
double pitch_mod;
double samplerate; // the (re)samplerate
double bitrate;
size_t start; // sets the start point from which to play
size_t end; // sets the end point
bool looping; // sets whether the sample should loop
bool reset; // resets the phase
int jitter; // jitter amount
double deviation;
};
// base class for accessing a sample buffer with adjustable samplerate, bitrate and other options.
class retro_buf {
public:
void set_host_samplerate(double _samplerate) {
m_host_samplerate = _samplerate;
m_imaging_filter_l.set_samplerate(_samplerate);
m_imaging_filter_r.set_samplerate(_samplerate);
}
void set_buf_samplerate(double _samplerate) {
m_buf_samplerate = _samplerate;
}
void set_buffer_size(size_t _buffer_size) {
m_buffer_size = _buffer_size;
}
void set_channel_count(size_t _channel_count) {
m_channel_count = _channel_count;
}
void start_playback() {
if (m_modulation.reset || (!m_modulation.reset && m_playback_pos == -1)) {
m_playback_pos = (double)m_modulation.start;
}
}
// @return is active
bool process_block(double** _outputs, size_t _block_size, retro_buf_modulation _mod) {
m_modulation = _mod;
for (int i = 0; i < _block_size; ++i) {
double output_l = 0;
double output_r = 0;
// if within bounds
if (m_playback_pos > -1 && m_playback_pos <= _mod.end) {
// quantize index
double samplerate_divisor = m_host_samplerate / _mod.samplerate;
size_t quantized_index = static_cast<size_t>(static_cast<size_t>(m_playback_pos / samplerate_divisor) * samplerate_divisor);
// get sample for each channel
output_l = get_sample((size_t)wrap(quantized_index + jitterize(_mod.jitter), m_buffer_size), 0);
if (m_channel_count > 0) {
output_r = get_sample((size_t)wrap(quantized_index + jitterize(_mod.jitter), m_buffer_size), 1);
} else {
output_r = output_l;
}
// advance position
double note_ratio = midi_to_ratio(_mod.midi_note + _mod.pitch_mod);
m_playback_pos += note_ratio * (m_buf_samplerate / m_host_samplerate);
reduce_bitrate(output_l, output_r, _mod.bitrate);
// calculate imaging filter frequency + deviation
double filter_frequency = ((_mod.samplerate / 2) * note_ratio) * ((_mod.deviation * 9) + 1);
m_imaging_filter_l.process_sample(output_l, filter_frequency);
m_imaging_filter_r.process_sample(output_r, filter_frequency);
}
// else if loop
else if(_mod.looping) {
// loop
m_playback_pos = (double)_mod.start;
}
// else
else {
// stop
m_playback_pos = -1;
}
_outputs[0][i] = output_l;
_outputs[1][i] = output_r;
}
return m_playback_pos > -1;
}
virtual float get_sample(size_t _index, size_t _channel) = 0;
private:
size_t m_channel_count = 0;
size_t m_buffer_size = 0;
double m_buf_samplerate = 44100.0;
double m_host_samplerate = 44100.0;
double m_playback_pos = -1;
chebyshev m_imaging_filter_l;
chebyshev m_imaging_filter_r;
ulaw m_compander;
retro_buf_modulation m_modulation;
float midi_to_ratio(double midi_note) {
return powf(powf(2, (float)midi_note - 60.f), 1.f / 12.f);
}
template <typename T>
T clamp(T& value, T min, T max) {
if (value < min) {
value = min;
} else if (value > max) {
value = max;
}
return value;
}
double wrap(double value, double max) {
while (value > max) {
value =- max;
}
return value;
}
int jitterize(int jitter) {
if (jitter > 0) {
return static_cast<int>(rand() % jitter);
} else {
return 0;
}
}
void reduce_bitrate(double& value1, double& value2, double bit) {
m_compander.encode_samples(value1, value2);
float resolution = powf(2, bit);
value1 = round(value1 * resolution) / resolution;
value2 = round(value2 * resolution) / resolution;
m_compander.decode_samples(value1, value2);
}
};
}