diff --git a/Makefile b/Makefile index ab24fc4..aec30d5 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ CFLAGS = $(shell $(PKGCONFIG) --cflags $(LIBS)) LDFLAGS = $(shell $(PKGCONFIG) --libs $(LIBS)) \ -Wall -march=native -Ofast -lm -lpthread -SRC = sampler.c sound.c soundconfig.c sample.c soundfile.c fx.c \ +SRC = sampler.c sound.c soundconfig.c sample.c fsample.c soundfile.c fx.c \ parse.c jl_ringbuf.c jl_mem.c ini.c init.c main.c OBJS = $(SRC:.c=.o) diff --git a/README.md b/README.md index b587af8..a7204bf 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,5 @@ TODO: -* In-memory storage of samples as int16 type. -* Tuning via tuning file +* Fake RC Layer for single layered sounds (or just layer mixing?) * Layer mixing -* Fake RC Layer for single layered sounds \ No newline at end of file diff --git a/const.h b/const.h index 43bddbd..b056ea7 100644 --- a/const.h +++ b/const.h @@ -19,5 +19,6 @@ bool gl_sustain; #define MAX_NAME 128 #define MAX_FX 8 #define MAX_FX_LINE 256 +#define MAX_INT16 32767 #endif diff --git a/fsample.c b/fsample.c new file mode 100644 index 0000000..52998d8 --- /dev/null +++ b/fsample.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include "jl_mem.h" +#include "soundfile.h" +#include "sample.h" +#include "fx.h" + +static bool starts_with(char *s, char *prefix) +{ + int n = strlen(prefix); + if(n > strlen(s)) { + return false; + } + + for(int i = 0; i < n; ++i) { + if(s[i] != prefix[i]) { + return false; + } + } + + return true; +} + +static void fsample_apply_effect(FSample *s, char *txt) +{ + // Attempt to parse each effect until one works. + if(starts_with(txt, "amp(")) { + double gain; + if(sscanf(txt, "amp(%lf)", &gain) != 1) { + goto apply_fx_err; + } + fx_amp(s, gain); + } + else if(starts_with(txt, "balance(")) { + double balance; + if(sscanf(txt, "balance(%lf)", &balance) != 1) { + goto apply_fx_err; + } + fx_balance(s, balance); + } + else if(starts_with(txt, "divide_rms(")) { + double t_ms; + if(sscanf(txt, "divide_rms(%lf)", &t_ms) != 1) { + goto apply_fx_err; + } + fx_divide_rms(s, t_ms); + } + else if(starts_with(txt, "fade_in(")) { + double t_ms; + if(sscanf(txt, "fade_in(%lf)", &t_ms) != 1) { + goto apply_fx_err; + } + fx_fade_in(s, t_ms); + } + else if(starts_with(txt, "ir(")) { + char path[MAX_FX_LINE]; + double mix; + if(sscanf(txt, "ir(%[^,], %lf)", path, &mix) != 2) { + goto apply_fx_err; + } + fx_ir(s, path, mix); + } + else if(starts_with(txt, "mono()")) { + fx_mono(s); + } + else if(starts_with(txt, "pan(")) { + double pan; + if(sscanf(txt, "pan(%lf)", &pan) != 1) { + goto apply_fx_err; + } + fx_pan(s, pan); + } + else if(starts_with(txt, "pre_cut(")) { + double pct, fade_ms; + if(sscanf(txt, "pre_cut(%lf, %lf)", &pct, &fade_ms) != 2) { + goto apply_fx_err; + } + fx_pre_cut(s, pct, fade_ms); + } + else if(starts_with(txt, "playback_speed(")) { + double speed; + if(sscanf(txt, "playback_speed(%lf)", &speed) != 1) { + goto apply_fx_err; + } + fx_playback_speed(s, speed); + } + else if(starts_with(txt, "pre_delay(")) { + double left_ms, right_ms; + if(sscanf(txt, "pre_delay(%lf, %lf)", &left_ms, &right_ms) != 2) { + goto apply_fx_err; + } + fx_pre_delay(s, left_ms, right_ms); + } + else if(starts_with(txt, "rc_highpass(")) { + double freq; + int order; + if(sscanf(txt, "rc_highpass(%lf, %d)", &freq, &order) != 2) { + goto apply_fx_err; + } + fx_rc_highpass(s, freq, order); + } + else if(starts_with(txt, "rc_lowpass(")) { + double freq; + int order; + if(sscanf(txt, "rc_lowpass(%lf, %d)", &freq, &order) != 2) { + goto apply_fx_err; + } + fx_rc_lowpass(s, freq, order); + } + else if(starts_with(txt, "rc_highshelf(")) { + double freq, gain; + int order; + if(sscanf(txt, "rc_highshelf(%lf, %d, %lf)", &freq, &order, &gain) != 3) { + goto apply_fx_err; + } + fx_rc_highshelf(s, freq, order, gain); + } + else if(starts_with(txt, "rc_lowshelf(")) { + double freq, gain; + int order; + if(sscanf(txt, "rc_lowshelf(%lf, %d, %lf)", &freq, &order, &gain) != 3) { + goto apply_fx_err; + } + fx_rc_lowshelf(s, freq, order, gain); + } + else if(starts_with(txt, "stereo_width(")) { + double width; + if(sscanf(txt, "stereo_width(%lf)", &width) != 1) { + goto apply_fx_err; + } + fx_stereo_width(s, width); + } + else { + printf("Unknown effect: %s\n", txt); + } + + return; + +apply_fx_err: + printf("Parsing error: %s\n", txt); + exit(1); +} + +void fsample_load(FSample *s, char *path, SoundConfig *conf, int buf_size) +{ + s->len = soundfile_load_stereo(path, &s->L, &s->R); + + // Apply global pre effects. + for(int i = 0; conf->global_fx_pre[i][0] != '\0'; ++i) { + fsample_apply_effect(s, conf->global_fx_pre[i]); + } + + // Apply pre effects. + for(int i = 0; conf->fx_pre[i][0] != '\0'; ++i) { + fsample_apply_effect(s, conf->fx_pre[i]); + } + + // Apply post effets. + for(int i = 0; conf->fx_post[i][0] != '\0'; ++i) { + fsample_apply_effect(s, conf->fx_post[i]); + } + + // Apply global post effects. + for(int i = 0; conf->global_fx_post[i][0] != '\0'; ++i) { + fsample_apply_effect(s, conf->global_fx_post[i]); + } +} + +void fsample_set_data(FSample *s, float *L, float *R, int len) +{ + jl_free(s->L); + jl_free(s->R); + s->L = L; + s->R = R; + s->len = len; +} + +double fsample_max_amp(FSample *s) +{ + double max = 0; + for(int i = 0; i < s->len; i++) { + if(max < s->L[i]) { + max = s->L[i]; + } else if(max < -s->L[i]) { + max = -s->L[i]; + } + + if(max < s->R[i]) { + max = s->R[i]; + } else if(max < -s->R[i]) { + max = -s->R[i]; + } + } + return max; +} + +void fsample_free(FSample *s) +{ + jl_free(s->L); + jl_free(s->R); + s->R = s->L = NULL; + s->len = 0; +} + diff --git a/fsample.h b/fsample.h new file mode 100644 index 0000000..d51b80c --- /dev/null +++ b/fsample.h @@ -0,0 +1,22 @@ +#ifndef fsample_HEADER_ +#define fsample_HEADER_ + +#include "soundconfig.h" + +// A floating point sample. +typedef struct { + int len; // The number of samples in each channel. + float *L; // The left channel. + float *R; // The right channel. +} FSample; + +// Load a sample. Allocates. +void fsample_load(FSample *s, char *path, SoundConfig *conf, int buf_size); +void fsample_set_data(FSample *s, float *L, float *R, int len); +double fsample_max_amp(FSample *s); + +// Free a sample. +void fsample_free(FSample *s); + + +#endif diff --git a/fx.c b/fx.c index dbeabfd..be7097e 100644 --- a/fx.c +++ b/fx.c @@ -8,14 +8,14 @@ #include "soundfile.h" #include "fx.h" -void fx_amp(Sample *s, double gain) { +void fx_amp(FSample *s, double gain) { for(int i = 0; i < s->len; ++i) { s->L[i] *= gain; s->R[i] *= gain; } } -void fx_balance(Sample *s, double balance) { +void fx_balance(FSample *s, double balance) { if(balance == 0) { return; } @@ -37,7 +37,7 @@ void fx_balance(Sample *s, double balance) { } } -void fx_divide_rms(Sample *s, double t_ms) { +void fx_divide_rms(FSample *s, double t_ms) { int di = t_ms * SAMPLE_RATE / 1000.0; if(di > s->len) { di = s->len; @@ -56,7 +56,7 @@ void fx_divide_rms(Sample *s, double t_ms) { fx_amp(s, 1.0 / rms); } -void fx_fade_in(Sample *s, double fade_ms) { +void fx_fade_in(FSample *s, double fade_ms) { int n_fade = fade_ms * SAMPLE_RATE / 1000.0; for(int i = 0; i < n_fade && i < s->len; ++i) { double val = (double)i / (double)n_fade; @@ -118,12 +118,12 @@ static float *rfft(int fft_len, fftw_complex *in, int out_len) { return f_out; } -void fx_ir(Sample *s, const char *path, double mix) { +void fx_ir(FSample *s, const char *path, double mix) { if(mix <= 0) { return; } - Sample ir; + FSample ir; ir.len = soundfile_load_stereo(path, &ir.L, &ir.R); int out_len = ir.len + s->len - 1; @@ -134,7 +134,7 @@ void fx_ir(Sample *s, const char *path, double mix) { fftw_complex *ir_L = fft(fft_len, ir.len, ir.L); fftw_complex *ir_R = fft(fft_len, ir.len, ir.R); - sample_free(&ir); + fsample_free(&ir); fftw_complex *L = fft(fft_len, s->len, s->L); fftw_complex *R = fft(fft_len, s->len, s->R); @@ -165,10 +165,10 @@ void fx_ir(Sample *s, const char *path, double mix) { jl_free(L); jl_free(R); - sample_set_data(s, L_wet, R_wet, out_len); + fsample_set_data(s, L_wet, R_wet, out_len); } -void fx_mono(Sample *s) { +void fx_mono(FSample *s) { for(int i = 0; i < s->len; ++i) { s->L[i] += s->R[i]; s->L[i] /= 2.0; @@ -176,35 +176,7 @@ void fx_mono(Sample *s) { } } -void fx_pad_multiple(Sample *s, int n) { - if(s->len % n == 0) { - return; - } - - // Compute new length. - int len = s->len + n; - len -= len % n; - - // Allocate arrays. - float *L = jl_malloc_exit(len * sizeof(float)); - float *R = jl_malloc_exit(len * sizeof(float)); - - // Zero padding samples. - for(int i = s->len; i < len; ++i) { - L[i] = 0; - R[i] = 0; - } - - // Copy data. - for(int i = 0; i < s->len; ++i) { - L[i] = s->L[i]; - R[i] = s->R[i]; - } - - sample_set_data(s, L, R, len); -} - -void fx_pan(Sample *s, double pan) { +void fx_pan(FSample *s, double pan) { if(pan < 0) { for(int i = 0; i < s->len; ++i) { s->L[i] += s->R[i] * -pan; @@ -224,7 +196,7 @@ static float interp(float *v, double idx) { return mu * v[i+1] + (1 - mu) * v[i]; } -void fx_playback_speed(Sample *s, double speed) { +void fx_playback_speed(FSample *s, double speed) { int len = s->len / speed; float *L = jl_malloc_exit(len * sizeof(float)); float *R = jl_malloc_exit(len * sizeof(float)); @@ -236,10 +208,10 @@ void fx_playback_speed(Sample *s, double speed) { idx += speed; } - sample_set_data(s, L, R, len); + fsample_set_data(s, L, R, len); } -void fx_pre_cut(Sample *s, double pct, double fade_ms) { +void fx_pre_cut(FSample *s, double pct, double fade_ms) { float th = pct / 100.0; int i_cut = 0; @@ -281,7 +253,7 @@ void fx_pre_cut(Sample *s, double pct, double fade_ms) { R[i] = s->R[i_cut + i]; } - sample_set_data(s, L, R, len); + fsample_set_data(s, L, R, len); fx_fade_in(s, fade_ms); } @@ -300,7 +272,7 @@ static float *delay_array(float *in, int in_len, int out_len, int delay) { return out; } -void fx_pre_delay(Sample *s, double left_ms, double right_ms) { +void fx_pre_delay(FSample *s, double left_ms, double right_ms) { int di_L = left_ms * SAMPLE_RATE / 1000; int di_R = right_ms * SAMPLE_RATE / 1000; @@ -310,14 +282,14 @@ void fx_pre_delay(Sample *s, double left_ms, double right_ms) { float *L = delay_array(s->L, s->len, out_len, di_L); float *R = delay_array(s->R, s->len, out_len, di_R); - sample_set_data(s, L, R, out_len); + fsample_set_data(s, L, R, out_len); } -void fx_rc_highpass(Sample *s, double freq, int order) { +void fx_rc_highpass(FSample *s, double freq, int order) { fx_rc_lowshelf(s, freq, order, -1); } -void fx_rc_lowpass(Sample *s, double freq, int order) { +void fx_rc_lowpass(FSample *s, double freq, int order) { fx_rc_highshelf(s, freq, order, -1); } @@ -351,7 +323,7 @@ static void rc_lowshelf1(float *data, int len, double freq, double gain) { } } -void fx_rc_highshelf(Sample *s, double freq, int order, double gain) { +void fx_rc_highshelf(FSample *s, double freq, int order, double gain) { freq = freq_3db(freq, order); for(int i = 0; i < order; ++i) { rc_highshelf1(s->L, s->len, freq, gain); @@ -359,7 +331,7 @@ void fx_rc_highshelf(Sample *s, double freq, int order, double gain) { } } -void fx_rc_lowshelf(Sample *s, double freq, int order, double gain) { +void fx_rc_lowshelf(FSample *s, double freq, int order, double gain) { freq = freq_3db(freq, order); for(int i = 0; i < order; ++i) { rc_lowshelf1(s->L, s->len, freq, gain); @@ -368,7 +340,7 @@ void fx_rc_lowshelf(Sample *s, double freq, int order, double gain) { } // See http://musicdsp.org/showArchiveComment.php?ArchiveID=256 -void fx_stereo_width(Sample *s, double width) { +void fx_stereo_width(FSample *s, double width) { float w2 = width * 0.5; float m, p; diff --git a/fx.h b/fx.h index 20d4f06..98d0448 100644 --- a/fx.h +++ b/fx.h @@ -1,23 +1,22 @@ #ifndef fx_HEADER_ #define fx_HEADER_ -#include "sample.h" +#include "fsample.h" -void fx_amp(Sample *s, double gain); -void fx_balance(Sample *s, double balance); -void fx_divide_rms(Sample *s, double t_ms); -void fx_fade_in(Sample *s, double t_ms); -void fx_ir(Sample *s, const char *path, double mix); -void fx_mono(Sample *s); -void fx_pad_multiple(Sample *s, int n); // INTERNAL ONLY. -void fx_pan(Sample *s, double pan); -void fx_playback_speed(Sample *s, double speed); -void fx_pre_cut(Sample *s, double pct, double fade_ms); -void fx_pre_delay(Sample *s, double left_ms, double right_ms); -void fx_rc_highpass(Sample *s, double freq, int order); -void fx_rc_lowpass(Sample *s, double freq, int order); -void fx_rc_highshelf(Sample *s, double freq, int order, double gain); -void fx_rc_lowshelf(Sample *s, double freq, int order, double gain); -void fx_stereo_width(Sample *s, double width); +void fx_amp(FSample *s, double gain); +void fx_balance(FSample *s, double balance); +void fx_divide_rms(FSample *s, double t_ms); +void fx_fade_in(FSample *s, double t_ms); +void fx_ir(FSample *s, const char *path, double mix); +void fx_mono(FSample *s); +void fx_pan(FSample *s, double pan); +void fx_playback_speed(FSample *s, double speed); +void fx_pre_cut(FSample *s, double pct, double fade_ms); +void fx_pre_delay(FSample *s, double left_ms, double right_ms); +void fx_rc_highpass(FSample *s, double freq, int order); +void fx_rc_lowpass(FSample *s, double freq, int order); +void fx_rc_highshelf(FSample *s, double freq, int order, double gain); +void fx_rc_lowshelf(FSample *s, double freq, int order, double gain); +void fx_stereo_width(FSample *s, double width); #endif diff --git a/init.c b/init.c index de6b92d..14bf0b2 100644 --- a/init.c +++ b/init.c @@ -44,7 +44,8 @@ static int sampler_handler(void *user, } else if(strcmp(name, "midi_max") == 0) { s->midi_max_vel = parse_int(value); - } else { + } + else { printf("Unknown setting.\n"); exit(1); } diff --git a/sample.c b/sample.c index c70c89f..36ea68a 100644 --- a/sample.c +++ b/sample.c @@ -5,178 +5,41 @@ #include #include #include "jl_mem.h" -#include "soundfile.h" +#include "fsample.h" #include "sample.h" -#include "fx.h" - -static bool starts_with(char *s, char *prefix) { - int n = strlen(prefix); - if(n > strlen(s)) { - return false; - } - - for(int i = 0; i < n; ++i) { - if(s[i] != prefix[i]) { - return false; - } - } - - return true; -} - -static void sample_apply_effect(Sample *s, char *txt) { - // Attempt to parse each effect until one works. - if(starts_with(txt, "amp(")) { - double gain; - if(sscanf(txt, "amp(%lf)", &gain) != 1) { - goto apply_fx_err; - } - fx_amp(s, gain); - } - else if(starts_with(txt, "balance(")) { - double balance; - if(sscanf(txt, "balance(%lf)", &balance) != 1) { - goto apply_fx_err; - } - fx_balance(s, balance); - } - else if(starts_with(txt, "divide_rms(")) { - double t_ms; - if(sscanf(txt, "divide_rms(%lf)", &t_ms) != 1) { - goto apply_fx_err; - } - fx_divide_rms(s, t_ms); - } - else if(starts_with(txt, "fade_in(")) { - double t_ms; - if(sscanf(txt, "fade_in(%lf)", &t_ms) != 1) { - goto apply_fx_err; - } - fx_fade_in(s, t_ms); - } - else if(starts_with(txt, "ir(")) { - char path[MAX_FX_LINE]; - double mix; - if(sscanf(txt, "ir(%[^,], %lf)", path, &mix) != 2) { - goto apply_fx_err; - } - fx_ir(s, path, mix); - } - else if(starts_with(txt, "mono()")) { - fx_mono(s); - } - else if(starts_with(txt, "pan(")) { - double pan; - if(sscanf(txt, "pan(%lf)", &pan) != 1) { - goto apply_fx_err; - } - fx_pan(s, pan); - } - else if(starts_with(txt, "pre_cut(")) { - double pct, fade_ms; - if(sscanf(txt, "pre_cut(%lf, %lf)", &pct, &fade_ms) != 2) { - goto apply_fx_err; - } - fx_pre_cut(s, pct, fade_ms); - } - else if(starts_with(txt, "playback_speed(")) { - double speed; - if(sscanf(txt, "playback_speed(%lf)", &speed) != 1) { - goto apply_fx_err; - } - fx_playback_speed(s, speed); - } - else if(starts_with(txt, "pre_delay(")) { - double left_ms, right_ms; - if(sscanf(txt, "pre_delay(%lf, %lf)", &left_ms, &right_ms) != 2) { - goto apply_fx_err; - } - fx_pre_delay(s, left_ms, right_ms); - } - else if(starts_with(txt, "rc_highpass(")) { - double freq; - int order; - if(sscanf(txt, "rc_highpass(%lf, %d)", &freq, &order) != 2) { - goto apply_fx_err; - } - fx_rc_highpass(s, freq, order); - } - else if(starts_with(txt, "rc_lowpass(")) { - double freq; - int order; - if(sscanf(txt, "rc_lowpass(%lf, %d)", &freq, &order) != 2) { - goto apply_fx_err; - } - fx_rc_lowpass(s, freq, order); - } - else if(starts_with(txt, "rc_highshelf(")) { - double freq, gain; - int order; - if(sscanf(txt, "rc_highshelf(%lf, %d, %lf)", &freq, &order, &gain) != 3) { - goto apply_fx_err; - } - fx_rc_highshelf(s, freq, order, gain); - } - else if(starts_with(txt, "rc_lowshelf(")) { - double freq, gain; - int order; - if(sscanf(txt, "rc_lowshelf(%lf, %d, %lf)", &freq, &order, &gain) != 3) { - goto apply_fx_err; - } - fx_rc_lowshelf(s, freq, order, gain); - } - else if(starts_with(txt, "stereo_width(")) { - double width; - if(sscanf(txt, "stereo_width(%lf)", &width) != 1) { - goto apply_fx_err; - } - fx_stereo_width(s, width); - } - else { - printf("Unknown effect: %s\n", txt); - } - - return; - -apply_fx_err: - printf("Parsing error: %s\n", txt); - exit(1); -} void sample_load(Sample *s, char *path, SoundConfig *conf, int buf_size) { - s->len = soundfile_load_stereo(path, &s->L, &s->R); + FSample fs; + fsample_load(&fs, path, conf, buf_size); - // Apply global pre effects. - for(int i = 0; conf->global_fx_pre[i][0] != '\0'; ++i) { - sample_apply_effect(s, conf->global_fx_pre[i]); - } + // Compute the new length. + int len = fs.len + buf_size; + len -= len % buf_size; - // Apply pre effects. - for(int i = 0; conf->fx_pre[i][0] != '\0'; ++i) { - sample_apply_effect(s, conf->fx_pre[i]); - } - - // Apply post effets. - for(int i = 0; conf->fx_post[i][0] != '\0'; ++i) { - sample_apply_effect(s, conf->fx_post[i]); - } - - // Apply global post effects. - for(int i = 0; conf->global_fx_post[i][0] != '\0'; ++i) { - sample_apply_effect(s, conf->global_fx_post[i]); - } - - // Pad to buffer size. - fx_pad_multiple(s, buf_size); -} - -void sample_set_data(Sample *s, float *L, float *R, int len) { - jl_free(s->L); - jl_free(s->R); - s->L = L; - s->R = R; + // Allocate new arrays. + s->L = jl_malloc_exit(len * sizeof(int16_t)); + s->R = jl_malloc_exit(len * sizeof(int16_t)); s->len = len; + + // Zero padding samples at end. + for(int i = fs.len; i < len; ++i) { + s->L[i] = s->R[i] = 0; + } + + double max = fsample_max_amp(&fs); + + // Use max value to compute gain. + s->gain = max / MAX_INT16; + + // Copy data. + for(int i = 0; i < fs.len; i++) { + s->L[i] = fs.L[i] / s->gain; + s->R[i] = fs.R[i] / s->gain; + } + + // Free the fsample data. + fsample_free(&fs); } void sample_free(Sample *s) @@ -186,3 +49,4 @@ void sample_free(Sample *s) s->R = s->L = NULL; s->len = 0; } + diff --git a/sample.h b/sample.h index 2469732..0086550 100644 --- a/sample.h +++ b/sample.h @@ -4,9 +4,10 @@ #include "soundconfig.h" typedef struct { - int len; // The number of samples in each channel. - float *L; // The left channel. - float *R; // The right channel. + int len; // The number of samples in each channel. + int16_t *L; // Left channel. + int16_t *R; // Right channel. + double gain; // Gain to apply when converting back to float. } Sample; // Load a sample. Allocates. diff --git a/sound.c b/sound.c index 6a41f37..440c2f6 100644 --- a/sound.c +++ b/sound.c @@ -99,18 +99,7 @@ void sound_load_samples(Sound *sound, int32_t buf_size) { * sound_trigger * *****************/ -void sound_trigger(Sound *sound, double vel) { - if(vel == 0) { - sound->key_down = false; - return; - } - - sound->key_down = true; - - if(sound->num_layers == 0) { - return; - } - +void sound_trigger_single(Sound *sound, double vel) { if(sound->num_playing == MAX_PLAYING) { // TODO: Log here. // No open slots left. @@ -125,9 +114,6 @@ void sound_trigger(Sound *sound, double vel) { ps->idx = 0; ps->tau = 1; - // Compute the playback amplitude. - ps->amp = pow(vel, sound->conf.gamma_amp); - // Get the appropriate layer. vel = pow(vel, sound->conf.gamma_layer); int layer = (int)((double)sound->num_layers * vel); @@ -141,11 +127,37 @@ void sound_trigger(Sound *sound, double vel) { // Chose the appropriate variation. ps->sample = &(sound->samples[layer][sound->rr_idx[layer]]); + // Compute the playback amplitude. + ps->amp = pow(vel, sound->conf.gamma_amp) * ps->sample->gain; + // Put playing sample into ring buffer. sound->ps_playing[sound->num_playing] = ps; sound->num_playing++; } +void sound_trigger_mix(Sound *sound, double vel) { + +} + +void sound_trigger(Sound *sound, double vel) { + if(vel == 0) { + sound->key_down = false; + return; + } + + sound->key_down = true; + + if(sound->num_layers == 0) { + return; + } + + if(sound->conf.mix_layers) { + // TODO! + } else { + sound_trigger_single(sound, vel); + } +} + /************* * sound_cut * *************/ @@ -223,38 +235,53 @@ static inline bool write_smpl_fast( } static inline bool write_smpl( - PlayingSample *ps, float *L, float *R, int32_t count) { + Sound *sound, PlayingSample *ps, + float *L, float *R, + float *amp_buf, int count) { - // TODO: We could pre-compute the amplitude array and then use SIMD - // instructions to speed this up. - float *sL = ps->sample->L; - float *sR = ps->sample->R; - double amp = ps->amp; - double tau = ps->tau; - int idx = ps->idx; + // Update tau for the playing sample based on key state. + switch(ps->state) { + case PS_OFF: + if(sound->key_down || gl_sustain) { + ps->state = PS_ON; + ps->tau = 1; + } + break; + case PS_ON: + if(!sound->key_down && !gl_sustain) { + ps->state = PS_OFF; + ps->tau = sound->conf.tau_off; + } + break; + } - // Note: We've ensured that each sample has a length that is a multiple of - // the buffer size and of 8, so this will always succeed. - for(int32_t i = 0; i < count; ++i) { - L[i] += sL[idx] * amp; - R[i] += sR[idx] * amp; + float amp = ps->amp; + float tau = ps->tau; + + for(int i = 0; i < count; ++i) { + amp_buf[i] = amp; amp *= tau; - idx += 1; } ps->amp = amp; - ps->idx = idx; + for(int i = 0; i < count; ++i) { + L[i] += ps->sample->L[ps->idx] * amp_buf[i]; + R[i] += ps->sample->R[ps->idx] * amp_buf[i]; + ps->idx += 1; + } + return ps->idx < ps->sample->len && ps->amp > MIN_AMP; } + void sound_write_buffer(Sound *sound, float *L, float *R) { int count = sound->num_playing; sound->num_playing = 0; for(int i = 0; i < count; ++i) { PlayingSample *ps = sound->ps_playing[i]; - if(write_smpl_fast(sound, ps, L, R, sound->amp_buf, sound->buf_size)) { + if(write_smpl(sound, ps, L, R, sound->amp_buf, sound->buf_size)) { sound->ps_playing[sound->num_playing] = ps; sound->num_playing++; } else { diff --git a/soundconfig.c b/soundconfig.c index 41d9930..b226b93 100644 --- a/soundconfig.c +++ b/soundconfig.c @@ -9,11 +9,12 @@ *****************************/ void soundconfig_load_defaults(SoundConfig *conf) { - conf->path[0] = '\0'; - conf->gamma_amp = 2.2; + conf->path[0] = '\0'; + conf->gamma_amp = 2.2; conf->gamma_layer = 1; - conf->tau_off = 1; - conf->tau_cut = proc_raw_tau_value(100); + conf->tau_off = 1; + conf->tau_cut = proc_raw_tau_value(100); + conf->mix_layers = false; for(int i = 0; i < MAX_FX; ++i) { conf->fx_pre[i][0] = '\0'; conf->fx_post[i][0] = '\0'; @@ -27,10 +28,11 @@ void soundconfig_load_defaults(SoundConfig *conf) { ********************/ void soundconfig_copy_globals(SoundConfig *to, SoundConfig *from) { - to->gamma_amp = from->gamma_amp; - to->gamma_layer = from->gamma_layer; - to->tau_off = from->tau_off; - to->tau_cut = from->tau_cut; + to->gamma_amp = from->gamma_amp; + to->gamma_layer = from->gamma_layer; + to->tau_off = from->tau_off; + to->tau_cut = from->tau_cut; + to->mix_layers = from->mix_layers; for(int i = 0; i < MAX_FX; ++i) { strncpy(to->global_fx_pre[i], from->fx_pre[i], MAX_FX_LINE); strncpy(to->global_fx_post[i], from->fx_post[i], MAX_FX_LINE); diff --git a/soundconfig.h b/soundconfig.h index d2ee7d9..12b480e 100644 --- a/soundconfig.h +++ b/soundconfig.h @@ -36,6 +36,10 @@ typedef struct { // tau_off. double tau_cut; + // If true, trigger two layers with proportional amplitudes when velocity + // lands between them. + bool mix_layers; + } SoundConfig;