This repository has been archived on 2017-07-22. You can view files and clone it, but cannot push or open issues/pull-requests.
jlsampler/sound.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]);
}
}
}