Commit acdc543c authored by ale's avatar ale

Initial support for timing/velocity humanization

With an algorithm pulled from drumgizmo.
parent a4d90f91
......@@ -5,7 +5,8 @@ noinst_LTLIBRARIES = libengine.la
libengine_la_SOURCES = \
engine/exponential_tables.h \
engine/adsr.c engine/adsr.h \
engine/voice.h engine/voice.c \
engine/voice.c engine/voice.h \
engine/humanize.c engine/humanize.h \
engine/drumkit.c engine/drumkit.h \
engine/sampler.c engine/sampler.h
libengine_la_LIBADD = $(SNDFILE_LIBS) $(SAMPLERATE_LIBS)
......
......@@ -386,6 +386,7 @@ static int parse_instruments(struct instrument **instruments,
for (i = 0; i < n; i++) {
instrument_set_defaults(&(*instruments)[i]);
(*instruments)[i].index = i;
if (!parse_instrument(&(*instruments)[i], nodes->nodeTab[i], xpathCtx, basedir, target_samplerate)) {
return 0;
}
......
......@@ -16,6 +16,7 @@ struct layer {
};
struct instrument {
int index;
char *name;
int note;
float envelope_attack, envelope_decay,
......
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include "humanize.h"
static inline int ms_to_samples(float ms, int samplerate) {
return (int)(ms / 1000.0 * samplerate);
}
static float gauss(void) {
float x = (float)random() / RAND_MAX,
y = (float)random() / RAND_MAX,
z = sqrtf(-2 * logf(x)) * cosf(2 * M_PI * y);
return z;
}
static float random_normal_distribution(float stddev) {
return gauss() * stddev;
}
// Initialize a humanizer (which tracks a single instrument).
void humanizer_init(struct humanizer *hum, struct humanizer_settings *settings) {
hum->settings = settings;
hum->latency_offset = 0;
hum->latency_last_pos = 0;
}
// Return the latency (number of samples) for the next event.
int humanize_latency_get_next_offset(struct humanizer *hum, int pos) {
int latency_max, latency_laid_back;
float duration, offset_ms;
latency_max = ms_to_samples(hum->settings->latency_max_ms, hum->settings->samplerate);
latency_laid_back = ms_to_samples(hum->settings->latency_laid_back_ms, hum->settings->samplerate);
duration = (float)(pos - hum->latency_last_pos) / hum->settings->samplerate;
hum->latency_offset *= pow(1.0 - hum->settings->latency_regain, duration);
hum->latency_last_pos = pos;
offset_ms = random_normal_distribution(hum->settings->latency_stddev_ms);
hum->latency_offset += ms_to_samples(offset_ms, hum->settings->samplerate);
if (hum->latency_offset < -latency_max)
hum->latency_offset = -latency_max;
if (hum->latency_offset > latency_max)
hum->latency_offset = latency_max;
return latency_max + latency_laid_back + hum->latency_offset;
}
int humanize_velocity(struct humanizer *hum, int velocity) {
float v;
int vo;
v = velocity / 127.0;
v *= 1 + random_normal_distribution(hum->settings->velocity_stddev);
vo = v * 127;
if (vo < 0)
vo = 0;
if (vo > 127)
vo = 127;
return vo;
}
// Initialize humanizer_settings with default (disabled) values.
void humanizer_settings_init(struct humanizer_settings *settings, int samplerate) {
settings->samplerate = samplerate;
settings->latency_regain = 1;
settings->latency_laid_back_ms = 0;
settings->latency_max_ms = 0;
settings->latency_stddev_ms = 0;
settings->velocity_stddev = 0;
}
// Returns the maximum latency introduced by the humanizer, so that
// plugins can compensate for it. This aligns the humanizer 0 time
// with latency_max_ms.
int humanize_get_latency(struct humanizer_settings *settings) {
return ms_to_samples(settings->latency_max_ms, settings->samplerate);
}
#ifndef __humanize_H
#define __humanize_H 1
struct humanizer_settings {
float samplerate;
float latency_regain;
float latency_laid_back_ms;
float latency_max_ms;
float latency_stddev_ms;
float velocity_stddev;
};
struct humanizer {
struct humanizer_settings *settings;
float latency_offset;
int latency_last_pos;
};
void humanizer_init(struct humanizer *, struct humanizer_settings *);
void humanizer_settings_init(struct humanizer_settings *, int);
int humanize_latency_get_next_offset(struct humanizer *, int);
int humanize_velocity(struct humanizer *, int);
int humanize_get_latency(struct humanizer_settings *);
#endif
......@@ -4,15 +4,21 @@
#include "sampler.h"
struct sampler *sampler_new(int num_voices, int samplerate) {
struct sampler *sampler = (struct sampler *)calloc(1, sizeof(struct sampler));
static void init_voices(struct sampler *sampler) {
int i;
for (i = 0; i < sampler->num_voices; i++) {
voice_init(&sampler->voices[i], sampler->samplerate);
}
}
struct sampler *sampler_new(int num_voices, int samplerate, struct humanizer_settings *hsettings) {
struct sampler *sampler = (struct sampler *)calloc(1, sizeof(struct sampler));
sampler->hsettings = hsettings;
sampler->samplerate = samplerate;
sampler->num_voices = num_voices;
sampler->voices = (struct voice *)calloc(num_voices, sizeof(struct voice));
for (i = 0; i < num_voices; i++) {
voice_init(&sampler->voices[i], samplerate);
}
init_voices(sampler);
return sampler;
}
......@@ -26,16 +32,26 @@ void sampler_free(struct sampler *sampler) {
}
void sampler_set_drumkit(struct sampler *sampler, struct drumkit *kit) {
int i;
if (sampler->kit) {
// Re-initialize all voices to avoid pending references to freed samples.
init_voices(sampler);
drumkit_free(sampler->kit);
free(sampler->kit);
free(sampler->humanizers);
}
sampler->kit = kit;
sampler->humanizers = (struct humanizer *)calloc(kit->num_instruments, sizeof(struct humanizer));
for (i = 0; i < kit->num_instruments; i++) {
humanizer_init(&sampler->humanizers[i], sampler->hsettings);
}
sampler->ticks = 0;
}
void sampler_noteon(struct sampler *sampler, int note, int velocity) {
struct instrument *ins;
int i;
int i, offset;
if (!sampler->kit)
return;
......@@ -66,8 +82,11 @@ void sampler_noteon(struct sampler *sampler, int note, int velocity) {
return;
}
// Humanize: modify preamble.
voice_noteon(voice, ins, note, velocity, 0);
// Humanize: modify preamble and velocity.
offset = humanize_latency_get_next_offset(&sampler->humanizers[ins->index], sampler->ticks);
velocity = humanize_velocity(&sampler->humanizers[ins->index], velocity);
voice_noteon(voice, ins, note, velocity, offset);
}
void sampler_noteoff(struct sampler *sampler, int note) {
......@@ -90,4 +109,10 @@ void sampler_output(struct sampler *sampler, float *out_l, float *out_r, int num
for (i = 0; i < sampler->num_voices; i++) {
voice_process(&sampler->voices[i], out_l, out_r, num_frames);
}
sampler->ticks += num_frames;
}
int sampler_get_latency(struct sampler *sampler) {
return humanize_get_latency(sampler->hsettings);
}
......@@ -3,18 +3,24 @@
#include "drumkit.h"
#include "voice.h"
#include "humanize.h"
struct sampler {
struct drumkit *kit;
int samplerate;
int num_voices;
struct voice *voices;
struct humanizer_settings *hsettings;
struct humanizer *humanizers;
int ticks;
};
struct sampler *sampler_new(int, int);
struct sampler *sampler_new(int, int, struct humanizer_settings *);
void sampler_set_drumkit(struct sampler *, struct drumkit *);
void sampler_free(struct sampler *);
void sampler_noteon(struct sampler *, int, int);
void sampler_noteoff(struct sampler *, int);
void sampler_output(struct sampler *, float *, float *, int);
int sampler_get_latency(struct sampler *);
#endif
......@@ -46,6 +46,7 @@ struct sampler_plugin {
plugin_uris uris;
struct humanizer_settings hsettings;
struct sampler *sampler;
int activated;
......@@ -148,7 +149,8 @@ static LV2_Handle instantiate(const LV2_Descriptor *descriptor, double rate,
struct sampler_plugin *plugin =
(struct sampler_plugin *)calloc(1, sizeof(struct sampler_plugin));
plugin->sampler = sampler_new(NUM_VOICES, (int)rate);
humanizer_settings_init(&plugin->hsettings, (int)rate);
plugin->sampler = sampler_new(NUM_VOICES, (int)rate, &plugin->hsettings);
plugin->samplerate = (int)rate;
// Get host features
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment