* Store samples as 16 bit int arrays in memory.

* Started work on layer mixing.
master
johnl 2017-07-22 20:16:41 +02:00
parent 0e9b7bbb94
commit 6bac68f84a
13 changed files with 377 additions and 278 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

208
fsample.c Normal file
View File

@ -0,0 +1,208 @@
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>
#include <string.h>
#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;
}

22
fsample.h Normal file
View File

@ -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

70
fx.c
View File

@ -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;

33
fx.h
View File

@ -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

3
init.c
View File

@ -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);
}

194
sample.c
View File

@ -5,178 +5,41 @@
#include <limits.h>
#include <string.h>
#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;
}

View File

@ -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.

89
sound.c
View File

@ -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 {

View File

@ -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);

View File

@ -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;