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; | 
 
    