diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/audio.c | 4 | ||||
-rw-r--r-- | src/bank.c | 141 |
2 files changed, 145 insertions, 0 deletions
diff --git a/src/audio.c b/src/audio.c index 95c727e..2a986b9 100644 --- a/src/audio.c +++ b/src/audio.c @@ -78,6 +78,10 @@ void xas_audio_apply_gain(xas_audio_format format, } } +void *xas_audio_buffer_alloc(xas_audio_format format, size_t buffer_size) { + return malloc(format.channels * format.sample_size * buffer_size); +} + static xas_audio_stream *stream_new(enum xas_audio_stream_type type, void *callback, xas_audio_cleanup cleanup, @@ -165,6 +165,28 @@ static xas_object_call_table call_table = { .destroy = (xas_object_destroy_callback)xas_bank_player_destroy }; +static int player_sola_init(xas_bank_player *player) { + size_t rate = player->bank->format.sample_rate, + buffer_size = (size_t)(player->bank->entry_size * XAS_BANK_PLAYER_DURATION_MAX), + total = buffer_size * player->bank->format.sample_size; + + player->sola_sequence = rate / (1000 / XAS_BANK_PLAYER_SOLA_SEQUENCE); + player->sola_overlap = rate / (1000 / XAS_BANK_PLAYER_SOLA_OVERLAP); + player->sola_window = rate / (1000 / XAS_BANK_PLAYER_SOLA_WINDOW); + player->sola_flat = rate / (1000 / XAS_BANK_PLAYER_SOLA_FLAT_DURATION); + + if ((player->buffer = malloc(total)) == NULL) { + goto error_malloc_buffer; + } + + player->buffer_size = buffer_size; + + return 0; + +error_malloc_buffer: + return -1; +} + xas_bank_player *xas_bank_player_new(xas_bank *bank) { xas_bank_player *player; @@ -181,8 +203,15 @@ xas_bank_player *xas_bank_player_new(xas_bank *bank) { player->entry = 0; player->index = 0; + if (player_sola_init(player) < 0) { + goto error_sola_init; + } + return player; +error_sola_init: + free(player); + error_malloc_player: return NULL; } @@ -224,7 +253,119 @@ void xas_bank_player_set_gain(xas_bank_player *player, float gain) { player->gain = gain; } +void xas_bank_player_set_duration(xas_bank_player *player, float factor) { + if (factor > XAS_BANK_PLAYER_DURATION_MAX) { + factor = XAS_BANK_PLAYER_DURATION_MAX; + } + + player->duration = factor; +} + +void xas_bank_player_set_pitch(xas_bank_player *player, float factor) { + player->pitch = factor; +} + +static size_t sola_best_overlap(xas_bank_player *player, + int16_t *samples_prev, + int16_t *samples_cur) { + size_t ret = 0; + + float bestcorr = -1e30f; + float temp[player->sola_overlap]; + + size_t i; + + /* + * Precalculate overlapping slopes with samples_prev + */ + for (i=0; i<player->sola_overlap; i++) { + temp[i] = (float)(samples_prev[i] * i * (player->sola_overlap - i)); + } + + // Find best overlap offset within [0..SEEK_WINDOW] + for (i=0; i<player->sola_window; i++) { + size_t j; + float crosscorr = 0; + + for (j=0; j<player->sola_overlap; j++) { + crosscorr += (float)samples_cur[i + j] * temp[j]; + } + + if (crosscorr > bestcorr) { + // found new best offset candidate + bestcorr = crosscorr; + ret = i; + } + } + + return ret; +} + +// Overlap 'input_prev' with 'input_new' by sliding the amplitudes during +// OVERLAP samples. Store result to 'output'. +static void sola_overlap(xas_bank_player *player, + int16_t *output, + int16_t *input_prev, + int16_t *input_new) { + size_t i; + + for (i=0; i<player->sola_overlap; i++) { + output[i] = (input_prev[i] * (player->sola_overlap - i) + + input_new[i] * i) / player->sola_overlap; + } +} + +static size_t sola_apply(xas_bank_player *player, + int16_t *output, + int16_t *input, + size_t count) { + size_t ret = 0; + + int16_t *seq_offset = input; + int16_t *prev_offset; + + size_t sequence_skip = XAS_BANK_PLAYER_SOLA_SEQUENCE_SKIP(player->duration), + sample_size = player->bank->format.sample_size; + + while (count > sequence_skip + player->sola_window) { + // copy flat mid-sequence from current processing sequence to output + memcpy(output, seq_offset, player->sola_flat * sample_size); + + // calculate a pointer to overlap at end of the processing sequence + prev_offset = seq_offset + player->sola_flat; + + // update input pointer to theoretical next processing sequence begin + input += sequence_skip - player->sola_overlap; + + // seek actual best matching offset using cross-correlation + seq_offset = input + sola_best_overlap(player, prev_offset, input); + + // do overlapping between previous & new sequence, copy result to output + sola_overlap(player, + output + player->sola_flat, + prev_offset, + seq_offset); + + // Update input & sequence pointers by overlapping amount + seq_offset += player->sola_overlap; + input += player->sola_overlap; + + // Update output pointer & sample counters + output += player->sola_sequence - player->sola_overlap; + ret += player->sola_sequence - player->sola_overlap; + count -= sequence_skip; + } + + return ret; +} + int xas_bank_player_start(xas_bank_player *player) { + if (player->flags & XAS_BANK_PLAYER_SCALE_TIME) { + + } else if (player->flags & XAS_BANK_PLAYER_SCALE_PITCH) { + + } + player->status = XAS_BANK_PLAYER_PLAYING; player->index = 0; |