209 lines
5.2 KiB
C
209 lines
5.2 KiB
C
#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;
|
|
}
|
|
|