forked from len0rd/rockbox
FS#8894 - Add time stretching feature to all SWCODEC targets - the current algorithm is best for spoken word.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21258 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
9e3255fdb0
commit
fb2380790e
40 changed files with 1027 additions and 261 deletions
319
apps/tdspeed.c
Normal file
319
apps/tdspeed.c
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2006 by Nicolas Pitre <nico@cam.org>
|
||||
* Copyright (C) 2006-2007 by Stéphane Doyon <s.doyon@videotron.ca>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
#include "debug.h"
|
||||
#include "system.h"
|
||||
#include "tdspeed.h"
|
||||
|
||||
#define assert(cond)
|
||||
|
||||
#define MIN_RATE 8000
|
||||
#define MAX_RATE 48000 /* double buffer for double rate */
|
||||
#define MINFREQ 100
|
||||
|
||||
#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */
|
||||
|
||||
struct tdspeed_state_s
|
||||
{
|
||||
bool stereo;
|
||||
int32_t shift_max; /* maximum displacement on a frame */
|
||||
int32_t src_step; /* source window pace */
|
||||
int32_t dst_step; /* destination window pace */
|
||||
int32_t dst_order; /* power of two for dst_step */
|
||||
int32_t ovl_shift; /* overlap buffer frame shift */
|
||||
int32_t ovl_size; /* overlap buffer used size */
|
||||
int32_t ovl_space; /* overlap buffer size */
|
||||
int32_t *ovl_buff[2]; /* overlap buffer */
|
||||
};
|
||||
static struct tdspeed_state_s tdspeed_state;
|
||||
|
||||
static int32_t *overlap_buffer[2] = { NULL, NULL };
|
||||
static int32_t *outbuf[2] = { NULL, NULL };
|
||||
|
||||
bool tdspeed_init(int samplerate, bool stereo, int factor)
|
||||
{
|
||||
struct tdspeed_state_s *st = &tdspeed_state;
|
||||
int src_frame_sz;
|
||||
|
||||
/* Allocate buffers */
|
||||
if (overlap_buffer[0] == NULL)
|
||||
overlap_buffer[0] = (int32_t *) buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t));
|
||||
if (overlap_buffer[1] == NULL)
|
||||
overlap_buffer[1] = (int32_t *) buffer_alloc(FIXED_BUFSIZE * sizeof(int32_t));
|
||||
if (outbuf[0] == NULL)
|
||||
outbuf[0] = (int32_t *) buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t));
|
||||
if (outbuf[1] == NULL)
|
||||
outbuf[1] = (int32_t *) buffer_alloc(TDSPEED_OUTBUFSIZE * sizeof(int32_t));
|
||||
|
||||
/* Check parameters */
|
||||
if (factor == 100)
|
||||
return false;
|
||||
if (samplerate < MIN_RATE || samplerate > MAX_RATE)
|
||||
return false;
|
||||
if (factor < SPEED_MIN || factor > SPEED_MAX)
|
||||
return false;
|
||||
|
||||
st->stereo = stereo;
|
||||
st->dst_step = samplerate / MINFREQ;
|
||||
|
||||
if (factor > 100)
|
||||
st->dst_step = st->dst_step * 100 / factor;
|
||||
st->dst_order = 1;
|
||||
|
||||
while (st->dst_step >>= 1)
|
||||
st->dst_order++;
|
||||
st->dst_step = (1 << st->dst_order);
|
||||
st->src_step = st->dst_step * factor / 100;
|
||||
st->shift_max = (st->dst_step > st->src_step) ? st->dst_step : st->src_step;
|
||||
|
||||
src_frame_sz = st->shift_max + st->dst_step;
|
||||
if (st->dst_step > st->src_step)
|
||||
src_frame_sz += st->dst_step - st->src_step;
|
||||
st->ovl_space = ((src_frame_sz - 2)/st->src_step) * st->src_step
|
||||
+ src_frame_sz;
|
||||
if (st->src_step > st->dst_step)
|
||||
st->ovl_space += 2*st->src_step - st->dst_step;
|
||||
|
||||
if (st->ovl_space > FIXED_BUFSIZE)
|
||||
st->ovl_space = FIXED_BUFSIZE;
|
||||
|
||||
st->ovl_size = 0;
|
||||
st->ovl_shift = 0;
|
||||
|
||||
st->ovl_buff[0] = overlap_buffer[0];
|
||||
if (stereo)
|
||||
st->ovl_buff[1] = overlap_buffer[1];
|
||||
else
|
||||
st->ovl_buff[1] = st->ovl_buff[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
|
||||
int data_len, int last, int out_size)
|
||||
/* data_len in samples */
|
||||
{
|
||||
struct tdspeed_state_s *st = &tdspeed_state;
|
||||
int32_t *curr, *prev, *dest[2], *d;
|
||||
int32_t i, j, next_frame, prev_frame, shift, src_frame_sz;
|
||||
bool stereo = buf_in[0] != buf_in[1];
|
||||
assert(stereo == st->stereo);
|
||||
|
||||
src_frame_sz = st->shift_max + st->dst_step;
|
||||
if (st->dst_step > st->src_step)
|
||||
src_frame_sz += st->dst_step - st->src_step;
|
||||
|
||||
/* deal with overlap data first, if any */
|
||||
if (st->ovl_size)
|
||||
{
|
||||
int32_t have, copy, steps;
|
||||
have = st->ovl_size;
|
||||
if (st->ovl_shift > 0)
|
||||
have -= st->ovl_shift;
|
||||
/* append just enough data to have all of the overlap buffer consumed */
|
||||
steps = (have - 1) / st->src_step;
|
||||
copy = steps * st->src_step + src_frame_sz - have;
|
||||
if (copy < src_frame_sz - st->dst_step)
|
||||
copy += st->src_step; /* one more step to allow for pregap data */
|
||||
if (copy > data_len) copy = data_len;
|
||||
assert(st->ovl_size +copy <= FIXED_BUFSIZE);
|
||||
memcpy(st->ovl_buff[0] + st->ovl_size, buf_in[0],
|
||||
copy * sizeof(int32_t));
|
||||
if (stereo)
|
||||
memcpy(st->ovl_buff[1] + st->ovl_size, buf_in[1],
|
||||
copy * sizeof(int32_t));
|
||||
if (!last && have + copy < src_frame_sz)
|
||||
{
|
||||
/* still not enough to process at least one frame */
|
||||
st->ovl_size += copy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* recursively call ourselves to process the overlap buffer */
|
||||
have = st->ovl_size;
|
||||
st->ovl_size = 0;
|
||||
if (copy == data_len)
|
||||
{
|
||||
assert( (have+copy) <= FIXED_BUFSIZE);
|
||||
return tdspeed_apply(buf_out, st->ovl_buff, have+copy, last,
|
||||
out_size);
|
||||
}
|
||||
assert( (have+copy) <= FIXED_BUFSIZE);
|
||||
i = tdspeed_apply(buf_out, st->ovl_buff, have+copy, -1, out_size);
|
||||
dest[0] = buf_out[0] + i;
|
||||
dest[1] = buf_out[1] + i;
|
||||
|
||||
/* readjust pointers to account for data already consumed */
|
||||
next_frame = copy - src_frame_sz + st->src_step;
|
||||
prev_frame = next_frame - st->ovl_shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest[0] = buf_out[0];
|
||||
dest[1] = buf_out[1];
|
||||
next_frame = prev_frame = 0;
|
||||
if (st->ovl_shift > 0)
|
||||
next_frame += st->ovl_shift;
|
||||
else
|
||||
prev_frame += -st->ovl_shift;
|
||||
}
|
||||
st->ovl_shift = 0;
|
||||
|
||||
/* process all complete frames */
|
||||
while (data_len - next_frame >= src_frame_sz)
|
||||
{
|
||||
/* find frame overlap by autocorelation */
|
||||
int64_t min_delta = ~(1ll << 63); /* most positive */
|
||||
shift = 0;
|
||||
#define INC1 8
|
||||
#define INC2 32
|
||||
/* Power of 2 of a 28bit number requires 56bits, can accumulate
|
||||
256times in a 64bit variable. */
|
||||
assert(st->dst_step / INC2 <= 256);
|
||||
assert(next_frame + st->shift_max - 1 + st->dst_step-1 < data_len);
|
||||
assert(prev_frame + st->dst_step - 1 < data_len);
|
||||
for (i = 0; i < st->shift_max; i += INC1)
|
||||
{
|
||||
int64_t delta = 0;
|
||||
curr = buf_in[0] + next_frame + i;
|
||||
prev = buf_in[0] + prev_frame;
|
||||
for (j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2)
|
||||
{
|
||||
int32_t diff = *curr - *prev;
|
||||
delta += (int64_t)diff * diff;
|
||||
if (delta >= min_delta)
|
||||
goto skip;
|
||||
}
|
||||
if (stereo)
|
||||
{
|
||||
curr = buf_in[1] +next_frame + i;
|
||||
prev = buf_in[1] +prev_frame;
|
||||
for (j = 0; j < st->dst_step; j += INC2, curr += INC2, prev += INC2)
|
||||
{
|
||||
int32_t diff = *curr - *prev;
|
||||
delta += (int64_t)diff * diff;
|
||||
if (delta >= min_delta)
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
min_delta = delta;
|
||||
shift = i;
|
||||
skip:;
|
||||
}
|
||||
|
||||
/* overlap fading-out previous frame with fading-in current frame */
|
||||
curr = buf_in[0] + next_frame + shift;
|
||||
prev = buf_in[0] + prev_frame;
|
||||
d = dest[0];
|
||||
assert(next_frame + shift + st->dst_step - 1 < data_len);
|
||||
assert(prev_frame + st->dst_step - 1 < data_len);
|
||||
assert(dest[0] - buf_out[0] + st->dst_step - 1 < out_size);
|
||||
for (i = 0, j = st->dst_step; j; i++, j--)
|
||||
{
|
||||
*d++ = (*curr++ * (int64_t)i
|
||||
+ *prev++ * (int64_t)j) >> st->dst_order;
|
||||
}
|
||||
dest[0] = d;
|
||||
if (stereo)
|
||||
{
|
||||
curr = buf_in[1] +next_frame + shift;
|
||||
prev = buf_in[1] +prev_frame;
|
||||
d = dest[1];
|
||||
for (i = 0, j = st->dst_step; j; i++, j--)
|
||||
{
|
||||
assert(d < buf_out[1] +out_size);
|
||||
*d++ = (*curr++ * (int64_t) i
|
||||
+ *prev++ * (int64_t) j) >> st->dst_order;
|
||||
}
|
||||
dest[1] = d;
|
||||
}
|
||||
|
||||
/* adjust pointers for next frame */
|
||||
prev_frame = next_frame + shift + st->dst_step;
|
||||
next_frame += st->src_step;
|
||||
|
||||
/* here next_frame - prev_frame = src_step - dst_step - shift */
|
||||
assert(next_frame - prev_frame == st->src_step - st->dst_step - shift);
|
||||
}
|
||||
|
||||
/* now deal with remaining partial frames */
|
||||
if (last == -1)
|
||||
{
|
||||
/* special overlap buffer processing: remember frame shift only */
|
||||
st->ovl_shift = next_frame - prev_frame;
|
||||
}
|
||||
else if (last != 0)
|
||||
{
|
||||
/* last call: purge all remaining data to output buffer */
|
||||
i = data_len -prev_frame;
|
||||
assert(dest[0] +i <= buf_out[0] +out_size);
|
||||
memcpy(dest[0], buf_in[0] +prev_frame, i * sizeof(int32_t));
|
||||
dest[0] += i;
|
||||
if (stereo)
|
||||
{
|
||||
assert(dest[1] +i <= buf_out[1] +out_size);
|
||||
memcpy(dest[1], buf_in[1] +prev_frame, i * sizeof(int32_t));
|
||||
dest[1] += i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* preserve remaining data + needed overlap data for next call */
|
||||
st->ovl_shift = next_frame - prev_frame;
|
||||
i = (st->ovl_shift < 0) ? next_frame : prev_frame;
|
||||
st->ovl_size = data_len - i;
|
||||
assert(st->ovl_size <= FIXED_BUFSIZE);
|
||||
memcpy(st->ovl_buff[0], buf_in[0]+i, st->ovl_size * sizeof(int32_t));
|
||||
if (stereo)
|
||||
memcpy(st->ovl_buff[1], buf_in[1]+i, st->ovl_size * sizeof(int32_t));
|
||||
}
|
||||
|
||||
return dest[0] - buf_out[0];
|
||||
}
|
||||
|
||||
long tdspeed_est_output_size()
|
||||
{
|
||||
return TDSPEED_OUTBUFSIZE;
|
||||
}
|
||||
|
||||
long tdspeed_est_input_size(long size)
|
||||
{
|
||||
struct tdspeed_state_s *st = &tdspeed_state;
|
||||
size = (size -st->ovl_size) *st->src_step / st->dst_step;
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
return size;
|
||||
}
|
||||
|
||||
int tdspeed_doit(int32_t *src[], int count)
|
||||
{
|
||||
count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] },
|
||||
src, count, 0, TDSPEED_OUTBUFSIZE);
|
||||
src[0] = outbuf[0];
|
||||
src[1] = outbuf[1];
|
||||
return count;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue