313 lines
7.0 KiB
C
313 lines
7.0 KiB
C
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <glob.h>
|
|
#include <x86intrin.h>
|
|
#include "jl_mem.h"
|
|
#include "sound.h"
|
|
|
|
/**************
|
|
* sound_init *
|
|
**************/
|
|
|
|
void sound_init(Sound *sound) {
|
|
sound->num_layers = 0;
|
|
|
|
for(int i = 0; i < MAX_LAYERS; ++i) {
|
|
sound->num_vars[i] = 0;
|
|
sound->rr_idx[i] = 0;
|
|
}
|
|
|
|
sound->key_down = false;
|
|
|
|
sound->buf_size = 0;
|
|
|
|
sound->num_free = 0;
|
|
sound->num_playing = 0;
|
|
|
|
for(int i = 0; i < MAX_PLAYING; ++i) {
|
|
sound->ps_free[i] = jl_malloc_exit(sizeof(PlayingSample));
|
|
sound->num_free++;
|
|
}
|
|
}
|
|
|
|
/**********************
|
|
* sound_load_samples *
|
|
**********************/
|
|
|
|
static void load_sound_sample(Sound *sound, char *path, int32_t buf_size) {
|
|
SoundConfig *conf = &(sound->conf);
|
|
|
|
char *path_end = &path[strlen(path) - 12];
|
|
int layer, var;
|
|
if(sscanf(path_end, "%d-%d.flac", &layer, &var) != 2) {
|
|
printf("Error parsing sample filename: %s\n", path);
|
|
exit(1);
|
|
}
|
|
|
|
if(layer >= MAX_LAYERS) {
|
|
printf("Layer is too large.");
|
|
exit(1);
|
|
}
|
|
|
|
if(var >= MAX_VARS) {
|
|
printf("Variation is too large.");
|
|
exit(1);
|
|
}
|
|
|
|
if(sound->num_layers < layer) {
|
|
sound->num_layers = layer;
|
|
}
|
|
|
|
sound->num_vars[layer-1] += 1;
|
|
Sample *sample = &sound->samples[layer-1][var-1];
|
|
sample_load(sample, path, conf, buf_size);
|
|
|
|
}
|
|
|
|
void sound_load_samples(Sound *sound, int32_t buf_size) {
|
|
sound->buf_size = buf_size;
|
|
sound->amp_buf = jl_malloc_exit(buf_size * sizeof(float));
|
|
|
|
char *path_glob = sound->conf.path;
|
|
|
|
if(strlen(path_glob) == 0) {
|
|
return;
|
|
}
|
|
|
|
glob_t results;
|
|
int ret = glob(path_glob, 0, NULL, &results);
|
|
if(ret == GLOB_NOMATCH) {
|
|
printf("No files found: %s\n", path_glob);
|
|
exit(1);
|
|
} else if(ret != 0) {
|
|
printf("Error matching files: %s\n", path_glob);
|
|
exit(1);
|
|
}
|
|
|
|
for(int i = 0; i < results.gl_pathc; i++) {
|
|
char *path = results.gl_pathv[i];
|
|
load_sound_sample(sound, path, buf_size);
|
|
}
|
|
|
|
globfree(&results);
|
|
}
|
|
|
|
/*****************
|
|
* sound_trigger *
|
|
*****************/
|
|
|
|
void sound_trigger_single(Sound *sound, double vel) {
|
|
if(sound->num_playing == MAX_PLAYING) {
|
|
// TODO: Log here.
|
|
// No open slots left.
|
|
return;
|
|
}
|
|
|
|
PlayingSample *ps = sound->ps_free[sound->num_free - 1];
|
|
sound->num_free--;
|
|
|
|
// Playback from the start with no fade out.
|
|
ps->state = PS_ON;
|
|
ps->idx = 0;
|
|
ps->tau = 1;
|
|
|
|
// Get the appropriate layer.
|
|
vel = pow(vel, sound->conf.gamma_layer);
|
|
int layer = (int)((double)sound->num_layers * vel);
|
|
if(layer == sound->num_layers) {
|
|
--layer;
|
|
}
|
|
|
|
// Update round-robin index.
|
|
sound->rr_idx[layer] = (sound->rr_idx[layer] + 1) % sound->num_vars[layer];
|
|
|
|
// 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 *
|
|
*************/
|
|
|
|
void sound_cut(Sound *sound) {
|
|
double tau = sound->conf.tau_cut;
|
|
if(tau == 1) {
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < sound->num_playing; ++i) {
|
|
PlayingSample *ps = sound->ps_playing[i];
|
|
ps->state = PS_CUT;
|
|
ps->tau = tau;
|
|
}
|
|
}
|
|
|
|
/**********************
|
|
* sound_write_buffer *
|
|
**********************/
|
|
|
|
static inline bool write_smpl_fast(
|
|
Sound *sound, PlayingSample *ps,
|
|
float *L, float *R,
|
|
float *amp_buf, int count) {
|
|
|
|
// 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;
|
|
}
|
|
|
|
float amp = ps->amp;
|
|
float tau = ps->tau;
|
|
|
|
for(int i = 0; i < count; ++i) {
|
|
amp_buf[i] = amp;
|
|
amp *= tau;
|
|
}
|
|
|
|
ps->amp = amp;
|
|
|
|
__m256 *v_amp = (__m256*)amp_buf;
|
|
|
|
__m256 *L_out = (__m256*)L;
|
|
__m256 *R_out = (__m256*)R;
|
|
|
|
__m256 *L_smp = (__m256*)ps->sample->L;
|
|
__m256 *R_smp = (__m256*)ps->sample->R;
|
|
|
|
int v_count = count / 8;
|
|
int idx = ps->idx / 8;
|
|
|
|
for(int i = 0; i < v_count; ++i) {
|
|
L_out[i] = _mm256_add_ps(
|
|
L_out[i], _mm256_mul_ps(L_smp[idx], v_amp[i]));
|
|
R_out[i] = _mm256_add_ps(
|
|
R_out[i], _mm256_mul_ps(R_smp[idx], v_amp[i]));
|
|
idx += 1;
|
|
}
|
|
|
|
ps->idx += count;
|
|
|
|
return ps->idx < ps->sample->len && ps->amp > MIN_AMP;
|
|
}
|
|
|
|
static inline bool write_smpl(
|
|
Sound *sound, PlayingSample *ps,
|
|
float *L, float *R,
|
|
float *amp_buf, int count) {
|
|
|
|
// 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;
|
|
}
|
|
|
|
float amp = ps->amp;
|
|
float tau = ps->tau;
|
|
|
|
for(int i = 0; i < count; ++i) {
|
|
amp_buf[i] = amp;
|
|
amp *= tau;
|
|
}
|
|
|
|
ps->amp = amp;
|
|
|
|
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(sound, ps, L, R, sound->amp_buf, sound->buf_size)) {
|
|
sound->ps_playing[sound->num_playing] = ps;
|
|
sound->num_playing++;
|
|
} else {
|
|
sound->ps_free[sound->num_free] = ps;
|
|
sound->num_free++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************
|
|
* sound_free *
|
|
**************/
|
|
|
|
void sound_free(Sound *sound) {
|
|
for(int i = 0; i < MAX_PLAYING; ++i) {
|
|
jl_free(sound->ps_free[i]);
|
|
}
|
|
|
|
for(int i = 0; i < MAX_PLAYING; ++i) {
|
|
jl_free(sound->ps_playing[i]);
|
|
}
|
|
|
|
for(int i = 0; i < MAX_LAYERS; ++i) {
|
|
for(int j = 0; j < sound->num_vars[i]; ++j) {
|
|
sample_free(&sound->samples[i][j]);
|
|
}
|
|
}
|
|
}
|