* Store samples as 16 bit int arrays in memory.
* Started work on layer mixing.master
parent
0e9b7bbb94
commit
6bac68f84a
2
Makefile
2
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)
|
||||
|
|
|
@ -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
|
1
const.h
1
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
70
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;
|
||||
|
||||
|
|
33
fx.h
33
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
|
||||
|
|
3
init.c
3
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);
|
||||
}
|
||||
|
|
194
sample.c
194
sample.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
7
sample.h
7
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.
|
||||
|
|
89
sound.c
89
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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Reference in New Issue