#include #include #include #include #include #include #include #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]); } } }