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
|
@ -126,6 +126,7 @@ pcmbuf.c
|
|||
playback.c
|
||||
codecs.c
|
||||
dsp.c
|
||||
tdspeed.c
|
||||
#ifdef HAVE_RECORDING
|
||||
enc_config.c
|
||||
#ifndef SIMULATOR
|
||||
|
|
|
@ -211,6 +211,8 @@ enum {
|
|||
ACTION_PS_TOGGLE_MODE,
|
||||
ACTION_PS_RESET,
|
||||
ACTION_PS_EXIT, /* _STD_* isnt going to work here */
|
||||
ACTION_PS_SLOWER,
|
||||
ACTION_PS_FASTER,
|
||||
|
||||
/* yesno screen */
|
||||
ACTION_YESNO_ACCEPT,
|
||||
|
|
298
apps/dsp.c
298
apps/dsp.c
|
@ -32,22 +32,19 @@
|
|||
#include "replaygain.h"
|
||||
#include "misc.h"
|
||||
#include "debug.h"
|
||||
#include "tdspeed.h"
|
||||
#include "buffer.h"
|
||||
|
||||
/* 16-bit samples are scaled based on these constants. The shift should be
|
||||
* no more than 15.
|
||||
*/
|
||||
#define WORD_SHIFT 12
|
||||
#define WORD_FRACBITS 27
|
||||
#define WORD_SHIFT 12
|
||||
#define WORD_FRACBITS 27
|
||||
|
||||
#define NATIVE_DEPTH 16
|
||||
/* If the buffer sizes change, check the assembly code! */
|
||||
#define SAMPLE_BUF_COUNT 256
|
||||
#define RESAMPLE_BUF_COUNT (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/
|
||||
#define DEFAULT_GAIN 0x01000000
|
||||
#define SAMPLE_BUF_LEFT_CHANNEL 0
|
||||
#define SAMPLE_BUF_RIGHT_CHANNEL (SAMPLE_BUF_COUNT/2)
|
||||
#define RESAMPLE_BUF_LEFT_CHANNEL 0
|
||||
#define RESAMPLE_BUF_RIGHT_CHANNEL (RESAMPLE_BUF_COUNT/2)
|
||||
#define NATIVE_DEPTH 16
|
||||
/* If the small buffer size changes, check the assembly code! */
|
||||
#define SMALL_SAMPLE_BUF_COUNT 256
|
||||
#define DEFAULT_GAIN 0x01000000
|
||||
|
||||
/* enums to index conversion properly with stereo mode and other settings */
|
||||
enum
|
||||
|
@ -101,7 +98,7 @@ struct dsp_data
|
|||
struct resample_data resample_data; /* 08h */
|
||||
int32_t clip_min; /* 18h */
|
||||
int32_t clip_max; /* 1ch */
|
||||
int32_t gain; /* 20h - Note that this is in S8.23 format. */
|
||||
int32_t gain; /* 20h - Note that this is in S8.23 format. */
|
||||
/* 24h */
|
||||
};
|
||||
|
||||
|
@ -140,11 +137,12 @@ struct eq_state
|
|||
|
||||
/* Typedefs keep things much neater in this case */
|
||||
typedef void (*sample_input_fn_type)(int count, const char *src[],
|
||||
int32_t *dst[]);
|
||||
int32_t *dst[]);
|
||||
typedef int (*resample_fn_type)(int count, struct dsp_data *data,
|
||||
int32_t *src[], int32_t *dst[]);
|
||||
const int32_t *src[], int32_t *dst[]);
|
||||
typedef void (*sample_output_fn_type)(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst);
|
||||
const int32_t *src[], int16_t *dst);
|
||||
|
||||
/* Single-DSP channel processing in place */
|
||||
typedef void (*channels_process_fn_type)(int count, int32_t *buf[]);
|
||||
/* DSP local channel processing in place */
|
||||
|
@ -163,6 +161,9 @@ struct dsp_config
|
|||
int sample_depth;
|
||||
int sample_bytes;
|
||||
int stereo_mode;
|
||||
bool tdspeed_enabled; /* User has enabled timestretch */
|
||||
int tdspeed_percent; /* Speed % */
|
||||
bool tdspeed_active; /* Timestretch is in use */
|
||||
int frac_bits;
|
||||
#ifdef HAVE_SW_TONE_CONTROLS
|
||||
/* Filter struct for software bass/treble controls */
|
||||
|
@ -218,16 +219,31 @@ static long album_peak;
|
|||
static long replaygain;
|
||||
static bool crossfeed_enabled;
|
||||
|
||||
#define audio_dsp (dsp_conf[CODEC_IDX_AUDIO])
|
||||
#define voice_dsp (dsp_conf[CODEC_IDX_VOICE])
|
||||
#define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO])
|
||||
#define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE])
|
||||
|
||||
/* The internal format is 32-bit samples, non-interleaved, stereo. This
|
||||
* format is similar to the raw output from several codecs, so the amount
|
||||
* of copying needed is minimized for that case.
|
||||
*/
|
||||
|
||||
int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR;
|
||||
static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR;
|
||||
#define RESAMPLE_RATIO 4 /* Enough for 11,025 Hz -> 44,100 Hz */
|
||||
|
||||
static int32_t small_sample_buf[SMALL_SAMPLE_BUF_COUNT] IBSS_ATTR;
|
||||
static int32_t small_resample_buf[SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO] IBSS_ATTR;
|
||||
|
||||
static int32_t *big_sample_buf = NULL;
|
||||
static int32_t *big_resample_buf = NULL;
|
||||
static int big_sample_buf_count = -1; /* -1=unknown, 0=not available */
|
||||
|
||||
static int sample_buf_count;
|
||||
static int32_t *sample_buf;
|
||||
static int32_t *resample_buf;
|
||||
|
||||
#define SAMPLE_BUF_LEFT_CHANNEL 0
|
||||
#define SAMPLE_BUF_RIGHT_CHANNEL (sample_buf_count/2)
|
||||
#define RESAMPLE_BUF_LEFT_CHANNEL 0
|
||||
#define RESAMPLE_BUF_RIGHT_CHANNEL (sample_buf_count/2 * RESAMPLE_RATIO)
|
||||
|
||||
#if 0
|
||||
/* Clip sample to arbitrary limits where range > 0 and min + range = max */
|
||||
|
@ -260,8 +276,66 @@ int sound_get_pitch(void)
|
|||
void sound_set_pitch(int permille)
|
||||
{
|
||||
pitch_ratio = permille;
|
||||
dsp_configure(&audio_dsp, DSP_SWITCH_FREQUENCY,
|
||||
audio_dsp.codec_frequency);
|
||||
dsp_configure(&AUDIO_DSP, DSP_SWITCH_FREQUENCY,
|
||||
AUDIO_DSP.codec_frequency);
|
||||
}
|
||||
|
||||
void tdspeed_setup(struct dsp_config *dspc)
|
||||
{
|
||||
if (dspc == &AUDIO_DSP)
|
||||
{
|
||||
dspc->tdspeed_active = false;
|
||||
if (!dspc->tdspeed_enabled)
|
||||
return;
|
||||
if (dspc->tdspeed_percent == 0)
|
||||
dspc->tdspeed_percent = 100;
|
||||
if (!tdspeed_init(
|
||||
dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency,
|
||||
dspc->stereo_mode != STEREO_MONO,
|
||||
dspc->tdspeed_percent))
|
||||
return;
|
||||
if (dspc->tdspeed_percent == 100 || big_sample_buf_count <= 0)
|
||||
return;
|
||||
dspc->tdspeed_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
void dsp_timestretch_enable(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
/* Set up timestretch buffers on first enable */
|
||||
if (big_sample_buf_count < 0)
|
||||
{
|
||||
big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO;
|
||||
big_sample_buf = small_resample_buf;
|
||||
big_resample_buf = (int32_t *) buffer_alloc(big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If not enabled at startup, buffers will never be available */
|
||||
if (big_sample_buf_count < 0)
|
||||
big_sample_buf_count = 0;
|
||||
}
|
||||
AUDIO_DSP.tdspeed_enabled = enable;
|
||||
tdspeed_setup(&AUDIO_DSP);
|
||||
}
|
||||
|
||||
void dsp_set_timestretch(int percent)
|
||||
{
|
||||
AUDIO_DSP.tdspeed_percent = percent;
|
||||
tdspeed_setup(&AUDIO_DSP);
|
||||
}
|
||||
|
||||
int dsp_get_timestretch()
|
||||
{
|
||||
return AUDIO_DSP.tdspeed_percent;
|
||||
}
|
||||
|
||||
bool dsp_timestretch_enabled()
|
||||
{
|
||||
return (AUDIO_DSP.tdspeed_enabled && big_sample_buf_count > 0);
|
||||
}
|
||||
|
||||
/* Convert count samples to the internal format, if needed. Updates src
|
||||
|
@ -403,10 +477,11 @@ static void sample_input_new_format(struct dsp_config *dsp)
|
|||
dsp->input_samples = sample_input_functions[convert];
|
||||
}
|
||||
|
||||
|
||||
#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
|
||||
/* write mono internal format to output format */
|
||||
static void sample_output_mono(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst)
|
||||
const int32_t *src[], int16_t *dst)
|
||||
{
|
||||
const int32_t *s0 = src[0];
|
||||
const int scale = data->output_scale;
|
||||
|
@ -425,7 +500,7 @@ static void sample_output_mono(int count, struct dsp_data *data,
|
|||
/* write stereo internal format to output format */
|
||||
#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
|
||||
static void sample_output_stereo(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst)
|
||||
const int32_t *src[], int16_t *dst)
|
||||
{
|
||||
const int32_t *s0 = src[0];
|
||||
const int32_t *s1 = src[1];
|
||||
|
@ -448,7 +523,7 @@ static void sample_output_stereo(int count, struct dsp_data *data,
|
|||
* This function handles mono and stereo outputs.
|
||||
*/
|
||||
static void sample_output_dithered(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst)
|
||||
const int32_t *src[], int16_t *dst)
|
||||
{
|
||||
const int32_t mask = dither_mask;
|
||||
const int32_t bias = dither_bias;
|
||||
|
@ -462,7 +537,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
|
|||
for (ch = 0; ch < data->num_channels; ch++)
|
||||
{
|
||||
struct dither_data * const dither = &dither_data[ch];
|
||||
int32_t *s = src[ch];
|
||||
const int32_t *s = src[ch];
|
||||
int i;
|
||||
|
||||
for (i = 0, d = &dst[ch]; i < count; i++, s++, d += 2)
|
||||
|
@ -540,7 +615,7 @@ static void sample_output_new_format(struct dsp_config *dsp)
|
|||
|
||||
int out = dsp->data.num_channels - 1;
|
||||
|
||||
if (dsp == &audio_dsp && dither_enabled)
|
||||
if (dsp == &AUDIO_DSP && dither_enabled)
|
||||
out += 2;
|
||||
|
||||
dsp->output_samples = sample_output_functions[out];
|
||||
|
@ -552,7 +627,7 @@ static void sample_output_new_format(struct dsp_config *dsp)
|
|||
*/
|
||||
#ifndef DSP_HAVE_ASM_RESAMPLING
|
||||
static int dsp_downsample(int count, struct dsp_data *data,
|
||||
int32_t *src[], int32_t *dst[])
|
||||
const int32_t *src[], int32_t *dst[])
|
||||
{
|
||||
int ch = data->num_channels - 1;
|
||||
uint32_t delta = data->resample_data.delta;
|
||||
|
@ -565,9 +640,9 @@ static int dsp_downsample(int count, struct dsp_data *data,
|
|||
/* Just initialize things and not worry too much about the relatively
|
||||
* uncommon case of not being able to spit out a sample for the frame.
|
||||
*/
|
||||
int32_t *s = src[ch];
|
||||
const int32_t *s = src[ch];
|
||||
int32_t last = data->resample_data.last_sample[ch];
|
||||
|
||||
|
||||
data->resample_data.last_sample[ch] = s[count - 1];
|
||||
d = dst[ch];
|
||||
phase = data->resample_data.phase;
|
||||
|
@ -593,7 +668,7 @@ static int dsp_downsample(int count, struct dsp_data *data,
|
|||
}
|
||||
|
||||
static int dsp_upsample(int count, struct dsp_data *data,
|
||||
int32_t *src[], int32_t *dst[])
|
||||
const int32_t *src[], int32_t *dst[])
|
||||
{
|
||||
int ch = data->num_channels - 1;
|
||||
uint32_t delta = data->resample_data.delta;
|
||||
|
@ -603,11 +678,10 @@ static int dsp_upsample(int count, struct dsp_data *data,
|
|||
/* Rolled channel loop actually showed slightly faster. */
|
||||
do
|
||||
{
|
||||
/* Should always be able to output a sample for a ratio up to
|
||||
RESAMPLE_BUF_COUNT / SAMPLE_BUF_COUNT. */
|
||||
int32_t *s = src[ch];
|
||||
/* Should always be able to output a sample for a ratio up to RESAMPLE_RATIO */
|
||||
const int32_t *s = src[ch];
|
||||
int32_t last = data->resample_data.last_sample[ch];
|
||||
|
||||
|
||||
data->resample_data.last_sample[ch] = s[count - 1];
|
||||
d = dst[ch];
|
||||
phase = data->resample_data.phase;
|
||||
|
@ -638,7 +712,7 @@ static int dsp_upsample(int count, struct dsp_data *data,
|
|||
|
||||
static void resampler_new_delta(struct dsp_config *dsp)
|
||||
{
|
||||
dsp->data.resample_data.delta = (unsigned long)
|
||||
dsp->data.resample_data.delta = (unsigned long)
|
||||
dsp->frequency * 65536LL / NATIVE_FREQUENCY;
|
||||
|
||||
if (dsp->frequency == NATIVE_FREQUENCY)
|
||||
|
@ -669,7 +743,7 @@ static inline int resample(struct dsp_config *dsp, int count, int32_t *src[])
|
|||
&resample_buf[RESAMPLE_BUF_RIGHT_CHANNEL],
|
||||
};
|
||||
|
||||
count = dsp->resample(count, &dsp->data, src, dst);
|
||||
count = dsp->resample(count, &dsp->data, (const int32_t **)src, dst);
|
||||
|
||||
src[0] = dst[0];
|
||||
src[1] = dst[dsp->data.num_channels - 1];
|
||||
|
@ -686,7 +760,7 @@ static void dither_init(struct dsp_config *dsp)
|
|||
|
||||
void dsp_dither_enable(bool enable)
|
||||
{
|
||||
struct dsp_config *dsp = &audio_dsp;
|
||||
struct dsp_config *dsp = &AUDIO_DSP;
|
||||
dither_enabled = enable;
|
||||
sample_output_new_format(dsp);
|
||||
}
|
||||
|
@ -705,7 +779,7 @@ static void apply_crossfeed(int count, int32_t *buf[])
|
|||
int32_t *coefs = &crossfeed_data.coefs[0];
|
||||
int32_t gain = crossfeed_data.gain;
|
||||
int32_t *di = crossfeed_data.index;
|
||||
|
||||
|
||||
int32_t acc;
|
||||
int32_t left, right;
|
||||
int i;
|
||||
|
@ -734,7 +808,7 @@ static void apply_crossfeed(int count, int32_t *buf[])
|
|||
/* Now add the attenuated direct sound and write to outputs */
|
||||
buf[0][i] = FRACMUL(left, gain) + hist_r[1];
|
||||
buf[1][i] = FRACMUL(right, gain) + hist_l[1];
|
||||
|
||||
|
||||
/* Wrap delay line index if bigger than delay line size */
|
||||
if (di >= delay + 13*2)
|
||||
di = delay;
|
||||
|
@ -754,7 +828,7 @@ static void apply_crossfeed(int count, int32_t *buf[])
|
|||
void dsp_set_crossfeed(bool enable)
|
||||
{
|
||||
crossfeed_enabled = enable;
|
||||
audio_dsp.apply_crossfeed = (enable && audio_dsp.data.num_channels > 1)
|
||||
AUDIO_DSP.apply_crossfeed = (enable && AUDIO_DSP.data.num_channels > 1)
|
||||
? apply_crossfeed : NULL;
|
||||
}
|
||||
|
||||
|
@ -815,17 +889,17 @@ static void set_gain(struct dsp_config *dsp)
|
|||
dsp->data.gain = DEFAULT_GAIN;
|
||||
|
||||
/* Replay gain not relevant to voice */
|
||||
if (dsp == &audio_dsp && replaygain)
|
||||
if (dsp == &AUDIO_DSP && replaygain)
|
||||
{
|
||||
dsp->data.gain = replaygain;
|
||||
}
|
||||
|
||||
|
||||
if (dsp->eq_process && eq_precut)
|
||||
{
|
||||
dsp->data.gain =
|
||||
(long) (((int64_t) dsp->data.gain * eq_precut) >> 24);
|
||||
}
|
||||
|
||||
|
||||
if (dsp->data.gain == DEFAULT_GAIN)
|
||||
{
|
||||
dsp->data.gain = 0;
|
||||
|
@ -846,7 +920,7 @@ static void set_gain(struct dsp_config *dsp)
|
|||
void dsp_set_eq_precut(int precut)
|
||||
{
|
||||
eq_precut = get_replaygain_int(precut * -10);
|
||||
set_gain(&audio_dsp);
|
||||
set_gain(&AUDIO_DSP);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -867,10 +941,10 @@ void dsp_set_eq_coefs(int band)
|
|||
cutoff = 0xffffffff / NATIVE_FREQUENCY * (*setting++);
|
||||
q = *setting++;
|
||||
gain = *setting++;
|
||||
|
||||
|
||||
if (q == 0)
|
||||
q = 1;
|
||||
|
||||
|
||||
/* NOTE: The coef functions assume the EMAC unit is in fractional mode,
|
||||
which it should be, since we're executed from the main thread. */
|
||||
|
||||
|
@ -903,7 +977,7 @@ static void eq_process(int count, int32_t *buf[])
|
|||
EQ_PEAK_SHIFT, /* peaking */
|
||||
EQ_SHELF_SHIFT, /* high shelf */
|
||||
};
|
||||
unsigned int channels = audio_dsp.data.num_channels;
|
||||
unsigned int channels = AUDIO_DSP.data.num_channels;
|
||||
int i;
|
||||
|
||||
/* filter configuration currently is 1 low shelf filter, 3 band peaking
|
||||
|
@ -925,14 +999,14 @@ static void eq_process(int count, int32_t *buf[])
|
|||
*/
|
||||
void dsp_set_eq(bool enable)
|
||||
{
|
||||
audio_dsp.eq_process = enable ? eq_process : NULL;
|
||||
set_gain(&audio_dsp);
|
||||
AUDIO_DSP.eq_process = enable ? eq_process : NULL;
|
||||
set_gain(&AUDIO_DSP);
|
||||
}
|
||||
|
||||
static void dsp_set_stereo_width(int value)
|
||||
{
|
||||
long width, straight, cross;
|
||||
|
||||
|
||||
width = value * 0x7fffff / 100;
|
||||
|
||||
if (value <= 100)
|
||||
|
@ -1039,14 +1113,14 @@ static void dsp_set_channel_config(int value)
|
|||
};
|
||||
|
||||
if ((unsigned)value >= ARRAYLEN(channels_process_functions) ||
|
||||
audio_dsp.stereo_mode == STEREO_MONO)
|
||||
AUDIO_DSP.stereo_mode == STEREO_MONO)
|
||||
{
|
||||
value = SOUND_CHAN_STEREO;
|
||||
}
|
||||
|
||||
/* This doesn't apply to voice */
|
||||
channels_mode = value;
|
||||
audio_dsp.channels_process = channels_process_functions[value];
|
||||
AUDIO_DSP.channels_process = channels_process_functions[value];
|
||||
}
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
|
@ -1057,10 +1131,10 @@ static void set_tone_controls(void)
|
|||
filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
|
||||
0xffffffff/NATIVE_FREQUENCY*3500,
|
||||
bass, treble, -prescale,
|
||||
audio_dsp.tone_filter.coefs);
|
||||
AUDIO_DSP.tone_filter.coefs);
|
||||
/* Sync the voice dsp coefficients */
|
||||
memcpy(&voice_dsp.tone_filter.coefs, audio_dsp.tone_filter.coefs,
|
||||
sizeof (voice_dsp.tone_filter.coefs));
|
||||
memcpy(&VOICE_DSP.tone_filter.coefs, AUDIO_DSP.tone_filter.coefs,
|
||||
sizeof (VOICE_DSP.tone_filter.coefs));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1069,7 +1143,8 @@ static void set_tone_controls(void)
|
|||
*/
|
||||
int dsp_callback(int msg, intptr_t param)
|
||||
{
|
||||
switch (msg) {
|
||||
switch (msg)
|
||||
{
|
||||
#ifdef HAVE_SW_TONE_CONTROLS
|
||||
case DSP_CALLBACK_SET_PRESCALE:
|
||||
prescale = param;
|
||||
|
@ -1112,7 +1187,6 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
|
|||
static long last_yield;
|
||||
long tick;
|
||||
int written = 0;
|
||||
int samples;
|
||||
|
||||
#if defined(CPU_COLDFIRE)
|
||||
/* set emac unit for dsp processing, and save old macsr, we're running in
|
||||
|
@ -1132,43 +1206,58 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
|
|||
will be preloaded to be used for the call if not. */
|
||||
while (count > 0)
|
||||
{
|
||||
samples = MIN(SAMPLE_BUF_COUNT/2, count);
|
||||
int samples = MIN(sample_buf_count/2, count);
|
||||
count -= samples;
|
||||
|
||||
dsp->input_samples(samples, src, tmp);
|
||||
|
||||
if (dsp->apply_gain)
|
||||
dsp->apply_gain(samples, &dsp->data, tmp);
|
||||
if (dsp->tdspeed_active)
|
||||
samples = tdspeed_doit(tmp, samples);
|
||||
|
||||
if (dsp->resample && (samples = resample(dsp, samples, tmp)) <= 0)
|
||||
break; /* I'm pretty sure we're downsampling here */
|
||||
int chunk_offset = 0;
|
||||
while (samples > 0)
|
||||
{
|
||||
int32_t *t2[2];
|
||||
t2[0] = tmp[0]+chunk_offset;
|
||||
t2[1] = tmp[1]+chunk_offset;
|
||||
|
||||
if (dsp->apply_crossfeed)
|
||||
dsp->apply_crossfeed(samples, tmp);
|
||||
int chunk = MIN(sample_buf_count/2, samples);
|
||||
chunk_offset += chunk;
|
||||
samples -= chunk;
|
||||
|
||||
if (dsp->eq_process)
|
||||
dsp->eq_process(samples, tmp);
|
||||
if (dsp->apply_gain)
|
||||
dsp->apply_gain(chunk, &dsp->data, t2);
|
||||
|
||||
if (dsp->resample && (chunk = resample(dsp, chunk, t2)) <= 0)
|
||||
break; /* I'm pretty sure we're downsampling here */
|
||||
|
||||
if (dsp->apply_crossfeed)
|
||||
dsp->apply_crossfeed(chunk, t2);
|
||||
|
||||
if (dsp->eq_process)
|
||||
dsp->eq_process(chunk, t2);
|
||||
|
||||
#ifdef HAVE_SW_TONE_CONTROLS
|
||||
if ((bass | treble) != 0)
|
||||
eq_filter(tmp, &dsp->tone_filter, samples,
|
||||
if ((bass | treble) != 0)
|
||||
eq_filter(t2, &dsp->tone_filter, chunk,
|
||||
dsp->data.num_channels, FILTER_BISHELF_SHIFT);
|
||||
#endif
|
||||
|
||||
if (dsp->channels_process)
|
||||
dsp->channels_process(samples, tmp);
|
||||
if (dsp->channels_process)
|
||||
dsp->channels_process(chunk, t2);
|
||||
|
||||
dsp->output_samples(samples, &dsp->data, tmp, (int16_t *)dst);
|
||||
dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst);
|
||||
|
||||
written += samples;
|
||||
dst += samples * sizeof (int16_t) * 2;
|
||||
|
||||
/* yield at least once each tick */
|
||||
tick = current_tick;
|
||||
if (TIME_AFTER(tick, last_yield))
|
||||
{
|
||||
last_yield = tick;
|
||||
yield();
|
||||
written += chunk;
|
||||
dst += chunk * sizeof (int16_t) * 2;
|
||||
|
||||
/* yield at least once each tick */
|
||||
tick = current_tick;
|
||||
if (TIME_AFTER(tick, last_yield))
|
||||
{
|
||||
last_yield = tick;
|
||||
yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1188,6 +1277,20 @@ int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
|
|||
/* dsp_input_size MUST be called afterwards */
|
||||
int dsp_output_count(struct dsp_config *dsp, int count)
|
||||
{
|
||||
if(!dsp->tdspeed_active)
|
||||
{
|
||||
sample_buf = small_sample_buf;
|
||||
resample_buf = small_resample_buf;
|
||||
sample_buf_count = SMALL_SAMPLE_BUF_COUNT;
|
||||
}
|
||||
else
|
||||
{
|
||||
sample_buf = big_sample_buf;
|
||||
sample_buf_count = big_sample_buf_count;
|
||||
resample_buf = big_resample_buf;
|
||||
}
|
||||
if(dsp->tdspeed_active)
|
||||
count = tdspeed_est_output_size();
|
||||
if (dsp->resample)
|
||||
{
|
||||
count = (int)(((unsigned long)count * NATIVE_FREQUENCY
|
||||
|
@ -1195,12 +1298,12 @@ int dsp_output_count(struct dsp_config *dsp, int count)
|
|||
}
|
||||
|
||||
/* Now we have the resampled sample count which must not exceed
|
||||
* RESAMPLE_BUF_COUNT/2 to avoid resample buffer overflow. One
|
||||
* RESAMPLE_BUF_RIGHT_CHANNEL to avoid resample buffer overflow. One
|
||||
* must call dsp_input_count() to get the correct input sample
|
||||
* count.
|
||||
*/
|
||||
if (count > RESAMPLE_BUF_COUNT/2)
|
||||
count = RESAMPLE_BUF_COUNT/2;
|
||||
if (count > RESAMPLE_BUF_RIGHT_CHANNEL)
|
||||
count = RESAMPLE_BUF_RIGHT_CHANNEL;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -1221,6 +1324,9 @@ int dsp_input_count(struct dsp_config *dsp, int count)
|
|||
dsp->data.resample_data.delta) >> 16);
|
||||
}
|
||||
|
||||
if(dsp->tdspeed_active)
|
||||
count = tdspeed_est_input_size(count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -1234,7 +1340,7 @@ static void dsp_update_functions(struct dsp_config *dsp)
|
|||
{
|
||||
sample_input_new_format(dsp);
|
||||
sample_output_new_format(dsp);
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp_set_crossfeed(crossfeed_enabled);
|
||||
}
|
||||
|
||||
|
@ -1246,9 +1352,9 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
switch (value)
|
||||
{
|
||||
case CODEC_IDX_AUDIO:
|
||||
return (intptr_t)&audio_dsp;
|
||||
return (intptr_t)&AUDIO_DSP;
|
||||
case CODEC_IDX_VOICE:
|
||||
return (intptr_t)&voice_dsp;
|
||||
return (intptr_t)&VOICE_DSP;
|
||||
default:
|
||||
return (intptr_t)NULL;
|
||||
}
|
||||
|
@ -1262,12 +1368,13 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
if we're called from the main audio thread. Voice UI thread should
|
||||
not need this feature.
|
||||
*/
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000;
|
||||
else
|
||||
dsp->frequency = dsp->codec_frequency;
|
||||
|
||||
resampler_new_delta(dsp);
|
||||
tdspeed_setup(dsp);
|
||||
break;
|
||||
|
||||
case DSP_SET_SAMPLE_DEPTH:
|
||||
|
@ -1290,13 +1397,14 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
|
||||
dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH;
|
||||
sample_input_new_format(dsp);
|
||||
dither_init(dsp);
|
||||
dither_init(dsp);
|
||||
break;
|
||||
|
||||
case DSP_SET_STEREO_MODE:
|
||||
dsp->stereo_mode = value;
|
||||
dsp->data.num_channels = value == STEREO_MONO ? 1 : 2;
|
||||
dsp_update_functions(dsp);
|
||||
tdspeed_setup(dsp);
|
||||
break;
|
||||
|
||||
case DSP_RESET:
|
||||
|
@ -1310,7 +1418,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
dsp->data.clip_min = -((1 << WORD_FRACBITS));
|
||||
dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY;
|
||||
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
{
|
||||
track_gain = 0;
|
||||
album_gain = 0;
|
||||
|
@ -1321,6 +1429,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
|
||||
dsp_update_functions(dsp);
|
||||
resampler_new_delta(dsp);
|
||||
tdspeed_setup(dsp);
|
||||
break;
|
||||
|
||||
case DSP_FLUSH:
|
||||
|
@ -1328,25 +1437,26 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
sizeof (dsp->data.resample_data));
|
||||
resampler_new_delta(dsp);
|
||||
dither_init(dsp);
|
||||
tdspeed_setup(dsp);
|
||||
break;
|
||||
|
||||
case DSP_SET_TRACK_GAIN:
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp_set_gain_var(&track_gain, value);
|
||||
break;
|
||||
|
||||
case DSP_SET_ALBUM_GAIN:
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp_set_gain_var(&album_gain, value);
|
||||
break;
|
||||
|
||||
case DSP_SET_TRACK_PEAK:
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp_set_gain_var(&track_peak, value);
|
||||
break;
|
||||
|
||||
case DSP_SET_ALBUM_PEAK:
|
||||
if (dsp == &audio_dsp)
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp_set_gain_var(&album_peak, value);
|
||||
break;
|
||||
|
||||
|
@ -1403,5 +1513,5 @@ void dsp_set_replaygain(void)
|
|||
|
||||
/* Store in S8.23 format to simplify calculations. */
|
||||
replaygain = gain;
|
||||
set_gain(&audio_dsp);
|
||||
set_gain(&AUDIO_DSP);
|
||||
}
|
||||
|
|
|
@ -164,5 +164,9 @@ void sound_set_pitch(int r);
|
|||
int sound_get_pitch(void);
|
||||
int dsp_callback(int msg, intptr_t param);
|
||||
void dsp_dither_enable(bool enable);
|
||||
void dsp_timestretch_enable(bool enable);
|
||||
void dsp_set_timestretch(int percent);
|
||||
bool dsp_timestretch_enabled(void);
|
||||
int dsp_get_timestretch(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -86,7 +86,7 @@ channels_process_sound_chan_karaoke:
|
|||
|
||||
/****************************************************************************
|
||||
* void sample_output_mono(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst)
|
||||
* const int32_t *src[], int16_t *dst)
|
||||
* NOTE: The following code processes two samples at once. When count is odd,
|
||||
* there is an additional obsolete sample processed, which will not be
|
||||
* used by the calling functions.
|
||||
|
@ -136,7 +136,7 @@ sample_output_mono:
|
|||
|
||||
/****************************************************************************
|
||||
* void sample_output_stereo(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst)
|
||||
* const int32_t *src[], int16_t *dst)
|
||||
* NOTE: The following code processes two samples at once. When count is odd,
|
||||
* there is an additional obsolete sample processed, which will not be
|
||||
* used by the calling functions.
|
||||
|
|
|
@ -54,9 +54,9 @@ void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]);
|
|||
|
||||
#ifdef DSP_HAVE_ASM_RESAMPLING
|
||||
int dsp_upsample(int count, struct dsp_data *data,
|
||||
int32_t *src[], int32_t *dst[]);
|
||||
const int32_t *src[], int32_t *dst[]);
|
||||
int dsp_downsample(int count, struct dsp_data *data,
|
||||
int32_t *src[], int32_t *dst[]);
|
||||
const int32_t *src[], int32_t *dst[]);
|
||||
#endif /* DSP_HAVE_ASM_RESAMPLING */
|
||||
|
||||
#ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO
|
||||
|
@ -73,12 +73,12 @@ void channels_process_sound_chan_karaoke(int count, int32_t *buf[]);
|
|||
|
||||
#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
|
||||
void sample_output_stereo(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst);
|
||||
const int32_t *src[], int16_t *dst);
|
||||
#endif
|
||||
|
||||
#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
|
||||
void sample_output_mono(int count, struct dsp_data *data,
|
||||
int32_t *src[], int16_t *dst);
|
||||
const int32_t *src[], int16_t *dst);
|
||||
#endif
|
||||
|
||||
#endif /* _DSP_ASM_H */
|
||||
|
|
|
@ -191,7 +191,7 @@ dsp_downsample:
|
|||
|
||||
/****************************************************************************
|
||||
* int dsp_upsample(int count, struct dsp_data *dsp,
|
||||
* int32_t *src[], int32_t *dst[])
|
||||
* const int32_t *src[], int32_t *dst[])
|
||||
*/
|
||||
.section .text
|
||||
.align 2
|
||||
|
@ -395,7 +395,7 @@ channels_process_sound_chan_karaoke:
|
|||
|
||||
/****************************************************************************
|
||||
* void sample_output_stereo(int count, struct dsp_data *data,
|
||||
* int32_t *src[], int16_t *dst)
|
||||
* const int32_t *src[], int16_t *dst)
|
||||
*
|
||||
* Framework based on the ubiquitous Rockbox line transfer logic for
|
||||
* Coldfire CPUs.
|
||||
|
@ -517,7 +517,7 @@ sample_output_stereo:
|
|||
|
||||
/****************************************************************************
|
||||
* void sample_output_mono(int count, struct dsp_data *data,
|
||||
* int32_t *src[], int16_t *dst)
|
||||
* const int32_t *src[], int16_t *dst)
|
||||
*
|
||||
* Same treatment as sample_output_stereo but for one channel.
|
||||
*/
|
||||
|
|
|
@ -36,12 +36,13 @@
|
|||
#include "system.h"
|
||||
#include "misc.h"
|
||||
#include "pitchscreen.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "tdspeed.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define PITCH_MODE_ABSOLUTE 1
|
||||
#define PITCH_MODE_SEMITONE -PITCH_MODE_ABSOLUTE
|
||||
#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
|
||||
/* on both sides when drawing */
|
||||
|
||||
|
||||
#define PITCH_MAX 2000
|
||||
#define PITCH_MIN 500
|
||||
|
@ -49,8 +50,14 @@
|
|||
#define PITCH_BIG_DELTA 10
|
||||
#define PITCH_NUDGE_DELTA 20
|
||||
|
||||
|
||||
static int pitch_mode = PITCH_MODE_ABSOLUTE; /* 1 - absolute, -1 - semitone */
|
||||
static enum
|
||||
{
|
||||
PITCH_MODE_ABSOLUTE,
|
||||
PITCH_MODE_SEMITONE,
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
PITCH_MODE_TIMESTRETCH,
|
||||
#endif
|
||||
} pitch_mode = PITCH_MODE_ABSOLUTE;
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -83,8 +90,8 @@ static void pitchscreen_fix_viewports(struct viewport *parent,
|
|||
|
||||
/* must be called before pitchscreen_draw, or within
|
||||
* since it neither clears nor updates the display */
|
||||
static void pitchscreen_draw_icons (struct screen *display,
|
||||
struct viewport *parent)
|
||||
static void pitchscreen_draw_icons(struct screen *display,
|
||||
struct viewport *parent)
|
||||
{
|
||||
display->set_viewport(parent);
|
||||
display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
|
||||
|
@ -102,25 +109,29 @@ static void pitchscreen_draw_icons (struct screen *display,
|
|||
display->update_viewport();
|
||||
}
|
||||
|
||||
static void pitchscreen_draw (struct screen *display, int max_lines,
|
||||
struct viewport pitch_viewports[PITCH_ITEM_COUNT], int pitch)
|
||||
static void pitchscreen_draw(struct screen *display, int max_lines,
|
||||
struct viewport pitch_viewports[PITCH_ITEM_COUNT],
|
||||
int pitch
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
,int speed
|
||||
#endif
|
||||
)
|
||||
{
|
||||
unsigned char* ptr;
|
||||
unsigned char buf[32];
|
||||
int width_val, w, h;
|
||||
char buf[32];
|
||||
int w, h;
|
||||
bool show_lang_pitch;
|
||||
|
||||
/* Hide "Pitch up/Pitch down" for a small screen */
|
||||
/* "Pitch up/Pitch down" - hide for a small screen */
|
||||
if (max_lines >= 5)
|
||||
{
|
||||
/* UP: Pitch Up */
|
||||
display->set_viewport(&pitch_viewports[PITCH_TOP]);
|
||||
if (pitch_mode == PITCH_MODE_ABSOLUTE) {
|
||||
ptr = str(LANG_PITCH_UP);
|
||||
} else {
|
||||
if (pitch_mode == PITCH_MODE_SEMITONE)
|
||||
ptr = str(LANG_PITCH_UP_SEMITONE);
|
||||
}
|
||||
display->getstringsize(ptr,&w,&h);
|
||||
else
|
||||
ptr = str(LANG_PITCH_UP);
|
||||
display->getstringsize(ptr, &w, &h);
|
||||
display->clear_viewport();
|
||||
/* draw text */
|
||||
display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
|
||||
|
@ -129,81 +140,125 @@ static void pitchscreen_draw (struct screen *display, int max_lines,
|
|||
|
||||
/* DOWN: Pitch Down */
|
||||
display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
|
||||
if (pitch_mode == PITCH_MODE_ABSOLUTE) {
|
||||
ptr = str(LANG_PITCH_DOWN);
|
||||
} else {
|
||||
if (pitch_mode == PITCH_MODE_SEMITONE)
|
||||
ptr = str(LANG_PITCH_DOWN_SEMITONE);
|
||||
}
|
||||
display->getstringsize(ptr,&w,&h);
|
||||
else
|
||||
ptr = str(LANG_PITCH_DOWN);
|
||||
display->getstringsize(ptr, &w, &h);
|
||||
display->clear_viewport();
|
||||
/* draw text */
|
||||
display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
|
||||
(w / 2), 0, ptr);
|
||||
display->update_viewport();
|
||||
}
|
||||
|
||||
/* Middle section */
|
||||
display->set_viewport(&pitch_viewports[PITCH_MID]);
|
||||
|
||||
snprintf((char *)buf, sizeof(buf), "%s", str(LANG_PITCH));
|
||||
display->getstringsize(buf,&w,&h);
|
||||
/* lets hide LANG_PITCH for smaller screens */
|
||||
display->clear_viewport();
|
||||
int width_used = 0;
|
||||
|
||||
/* Middle section upper line - hide for a small screen */
|
||||
if ((show_lang_pitch = (max_lines >= 3)))
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||
0, buf);
|
||||
|
||||
/* "XXX.X%" */
|
||||
snprintf((char *)buf, sizeof(buf), "%d.%d%%",
|
||||
pitch / 10, pitch % 10 );
|
||||
display->getstringsize(buf,&width_val,&h);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (width_val / 2),
|
||||
(show_lang_pitch? h : h/2), buf);
|
||||
|
||||
/* What's wider? LANG_PITCH or the value?
|
||||
* Only interesting if LANG_PITCH is actually drawn */
|
||||
if (show_lang_pitch && width_val > w)
|
||||
w = width_val;
|
||||
|
||||
/* Let's treat '+' and '-' as equally wide
|
||||
* This saves a getstringsize call
|
||||
* Also, it wouldn't look nice if -2% shows up, but +2% not */
|
||||
display->getstringsize("+2%",&width_val,&h);
|
||||
w += width_val*2;
|
||||
/* hide +2%/-2% for a narrow screens */
|
||||
if (w <= pitch_viewports[PITCH_MID].width)
|
||||
{
|
||||
/* RIGHT: +2% */
|
||||
display->putsxy(pitch_viewports[PITCH_MID].width - width_val, h /2, "+2%");
|
||||
/* LEFT: -2% */
|
||||
display->putsxy(0, h / 2, "-2%");
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode != PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
#endif
|
||||
/* LANG_PITCH */
|
||||
snprintf(buf, sizeof(buf), "%s", str(LANG_PITCH));
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Pitch:XXX.X% */
|
||||
snprintf(buf, sizeof(buf), "%s:%d.%d%%", str(LANG_PITCH),
|
||||
pitch / 10, pitch % 10);
|
||||
}
|
||||
#endif
|
||||
display->getstringsize(buf, &w, &h);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||
0, buf);
|
||||
if (w > width_used)
|
||||
width_used = w;
|
||||
}
|
||||
|
||||
/* Middle section lower line */
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode != PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
#endif
|
||||
/* "XXX.X%" */
|
||||
snprintf(buf, sizeof(buf), "%d.%d%%",
|
||||
pitch / 10, pitch % 10);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
}
|
||||
else
|
||||
{
|
||||
/* "Speed:XXX%" */
|
||||
snprintf(buf, sizeof(buf), "%s:%d%%", str(LANG_SPEED), speed);
|
||||
}
|
||||
#endif
|
||||
display->getstringsize(buf, &w, &h);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||
(show_lang_pitch ? h : h/2), buf);
|
||||
if (w > width_used)
|
||||
width_used = w;
|
||||
|
||||
/* Middle section left/right labels */
|
||||
const char *leftlabel = "-2%";
|
||||
const char *rightlabel = "+2%";
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode == PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
leftlabel = "<<";
|
||||
rightlabel = ">>";
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Only display if they fit */
|
||||
display->getstringsize(leftlabel, &w, &h);
|
||||
width_used += w;
|
||||
display->getstringsize(rightlabel, &w, &h);
|
||||
width_used += w;
|
||||
|
||||
if (width_used <= pitch_viewports[PITCH_MID].width)
|
||||
{
|
||||
display->putsxy(0, h / 2, leftlabel);
|
||||
display->putsxy(pitch_viewports[PITCH_MID].width - w, h /2, rightlabel);
|
||||
}
|
||||
display->update_viewport();
|
||||
display->set_viewport(NULL);
|
||||
}
|
||||
|
||||
static int pitch_increase(int pitch, int delta, bool allow_cutoff)
|
||||
static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
|
||||
{
|
||||
int new_pitch;
|
||||
|
||||
if (delta < 0) {
|
||||
if (pitch + delta >= PITCH_MIN) {
|
||||
new_pitch = pitch + delta;
|
||||
} else {
|
||||
if (!allow_cutoff) {
|
||||
if (pitch_delta < 0)
|
||||
{
|
||||
if (pitch + pitch_delta >= PITCH_MIN)
|
||||
new_pitch = pitch + pitch_delta;
|
||||
else
|
||||
{
|
||||
if (!allow_cutoff)
|
||||
return pitch;
|
||||
}
|
||||
new_pitch = PITCH_MIN;
|
||||
}
|
||||
} else if (delta > 0) {
|
||||
if (pitch + delta <= PITCH_MAX) {
|
||||
new_pitch = pitch + delta;
|
||||
} else {
|
||||
if (!allow_cutoff) {
|
||||
}
|
||||
else if (pitch_delta > 0)
|
||||
{
|
||||
if (pitch + pitch_delta <= PITCH_MAX)
|
||||
new_pitch = pitch + pitch_delta;
|
||||
else
|
||||
{
|
||||
if (!allow_cutoff)
|
||||
return pitch;
|
||||
}
|
||||
new_pitch = PITCH_MAX;
|
||||
}
|
||||
} else {
|
||||
/* delta == 0 -> no real change */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pitch_delta == 0 -> no real change */
|
||||
return pitch;
|
||||
}
|
||||
sound_set_pitch(new_pitch);
|
||||
|
@ -234,10 +289,13 @@ static int pitch_increase_semitone(int pitch, bool up)
|
|||
uint32_t tmp;
|
||||
uint32_t round_fct; /* How much to scale down at the end */
|
||||
tmp = pitch;
|
||||
if (up) {
|
||||
if (up)
|
||||
{
|
||||
tmp = tmp * PITCH_SEMITONE_FACTOR;
|
||||
round_fct = PITCH_K_FCT;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR;
|
||||
round_fct = PITCH_N_FCT;
|
||||
}
|
||||
|
@ -256,7 +314,12 @@ int gui_syncpitchscreen_run(void)
|
|||
{
|
||||
int button, i;
|
||||
int pitch = sound_get_pitch();
|
||||
int new_pitch, delta = 0;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
int speed = dsp_get_timestretch();
|
||||
int maintain_speed_pitch = speed * pitch; /* speed * pitch to maintain */
|
||||
#endif
|
||||
int new_pitch;
|
||||
int pitch_delta = 0;
|
||||
bool nudged = false;
|
||||
bool exit = false;
|
||||
/* should maybe be passed per parameter later, not needed for now */
|
||||
|
@ -283,58 +346,118 @@ int gui_syncpitchscreen_run(void)
|
|||
{
|
||||
FOR_NB_SCREENS(i)
|
||||
pitchscreen_draw(&screens[i], max_lines[i],
|
||||
pitch_viewports[i], pitch);
|
||||
button = get_action(CONTEXT_PITCHSCREEN,HZ);
|
||||
switch (button) {
|
||||
pitch_viewports[i], pitch
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
button = get_action(CONTEXT_PITCHSCREEN, HZ);
|
||||
switch (button)
|
||||
{
|
||||
case ACTION_PS_INC_SMALL:
|
||||
delta = PITCH_SMALL_DELTA;
|
||||
pitch_delta = PITCH_SMALL_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_INC_BIG:
|
||||
delta = PITCH_BIG_DELTA;
|
||||
pitch_delta = PITCH_BIG_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_DEC_SMALL:
|
||||
delta = -PITCH_SMALL_DELTA;
|
||||
pitch_delta = -PITCH_SMALL_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_DEC_BIG:
|
||||
delta = -PITCH_BIG_DELTA;
|
||||
pitch_delta = -PITCH_BIG_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_NUDGE_RIGHT:
|
||||
new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
|
||||
nudged = (new_pitch != pitch);
|
||||
pitch = new_pitch;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode != PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
#endif
|
||||
new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
|
||||
nudged = (new_pitch != pitch);
|
||||
pitch = new_pitch;
|
||||
break;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
}
|
||||
|
||||
case ACTION_PS_FASTER:
|
||||
if (pitch_mode == PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
if (speed < SPEED_MAX)
|
||||
{
|
||||
speed++;
|
||||
dsp_set_timestretch(speed);
|
||||
maintain_speed_pitch = speed * pitch;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ACTION_PS_NUDGE_RIGHTOFF:
|
||||
if (nudged) {
|
||||
if (nudged)
|
||||
{
|
||||
pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
|
||||
nudged = false;
|
||||
}
|
||||
nudged = false;
|
||||
break;
|
||||
|
||||
case ACTION_PS_NUDGE_LEFT:
|
||||
new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
|
||||
nudged = (new_pitch != pitch);
|
||||
pitch = new_pitch;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode != PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
#endif
|
||||
new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
|
||||
nudged = (new_pitch != pitch);
|
||||
pitch = new_pitch;
|
||||
break;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
}
|
||||
|
||||
case ACTION_PS_SLOWER:
|
||||
if (pitch_mode == PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
if (speed > SPEED_MIN)
|
||||
{
|
||||
speed--;
|
||||
dsp_set_timestretch(speed);
|
||||
maintain_speed_pitch = speed * pitch;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ACTION_PS_NUDGE_LEFTOFF:
|
||||
if (nudged) {
|
||||
if (nudged)
|
||||
{
|
||||
pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
|
||||
nudged = false;
|
||||
}
|
||||
nudged = false;
|
||||
break;
|
||||
|
||||
case ACTION_PS_RESET:
|
||||
pitch = 1000;
|
||||
sound_set_pitch( pitch );
|
||||
sound_set_pitch(pitch);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
speed = 100;
|
||||
dsp_set_timestretch(speed);
|
||||
maintain_speed_pitch = speed * pitch;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ACTION_PS_TOGGLE_MODE:
|
||||
pitch_mode = -pitch_mode;
|
||||
++pitch_mode;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (dsp_timestretch_enabled())
|
||||
{
|
||||
if (pitch_mode > PITCH_MODE_TIMESTRETCH)
|
||||
pitch_mode = PITCH_MODE_ABSOLUTE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (pitch_mode > PITCH_MODE_SEMITONE)
|
||||
pitch_mode = PITCH_MODE_ABSOLUTE;
|
||||
break;
|
||||
|
||||
case ACTION_PS_EXIT:
|
||||
|
@ -342,19 +465,32 @@ int gui_syncpitchscreen_run(void)
|
|||
break;
|
||||
|
||||
default:
|
||||
if(default_event_handler(button) == SYS_USB_CONNECTED)
|
||||
if (default_event_handler(button) == SYS_USB_CONNECTED)
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
if(delta)
|
||||
if (pitch_delta)
|
||||
{
|
||||
if (pitch_mode == PITCH_MODE_ABSOLUTE) {
|
||||
pitch = pitch_increase(pitch, delta, true);
|
||||
} else {
|
||||
pitch = pitch_increase_semitone(pitch, delta > 0);
|
||||
if (pitch_mode == PITCH_MODE_SEMITONE)
|
||||
pitch = pitch_increase_semitone(pitch, pitch_delta > 0);
|
||||
else
|
||||
pitch = pitch_increase(pitch, pitch_delta, true);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode == PITCH_MODE_TIMESTRETCH)
|
||||
{
|
||||
/* Set speed to maintain time dimension */
|
||||
/* i.e. increase pitch, slow down speed */
|
||||
int new_speed = maintain_speed_pitch / pitch;
|
||||
if (new_speed >= SPEED_MIN && new_speed <= SPEED_MAX)
|
||||
{
|
||||
speed = new_speed;
|
||||
dsp_set_timestretch(speed);
|
||||
}
|
||||
}
|
||||
|
||||
delta = 0;
|
||||
else
|
||||
maintain_speed_pitch = speed * pitch;
|
||||
#endif
|
||||
pitch_delta = 0;
|
||||
}
|
||||
}
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
|
|
|
@ -159,6 +159,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_F1, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_ON, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_OFF, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -174,6 +174,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
|
|
@ -194,19 +194,17 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
|
|||
static const struct button_mapping button_context_pitchscreen[] = {
|
||||
{ ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_PS_INC_BIG, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_TOGGLE_MODE, BUTTON_REC, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
||||
|
|
|
@ -182,19 +182,19 @@ static const struct button_mapping button_context_settings_right_is_inc[] = {
|
|||
}; /* button_context_settings_right_is_inc */
|
||||
|
||||
static const struct button_mapping button_context_pitchscreen[] = {
|
||||
{ ACTION_PS_INC_SMALL, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_INC_BIG, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_SMALL, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_BIG, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_TOGGLE_MODE, BUTTON_HOME, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_PS_INC_BIG, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE },
|
||||
{ ACTION_PS_DEC_BIG, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_TOGGLE_MODE, BUTTON_HOME, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
||||
|
|
|
@ -203,6 +203,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT,BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
|
|
@ -203,6 +203,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_UP, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
|
|
@ -252,6 +252,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_BACK, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT,BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
@ -445,6 +447,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_RESET, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY },
|
||||
{ ACTION_PS_TOGGLE_MODE, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_DSP|BUTTON_REL, BUTTON_RC_DSP },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_RW|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST
|
||||
}; /* remote_button_context_pitchscreen */
|
||||
|
|
|
@ -240,6 +240,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_A, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -260,6 +260,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_LEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
@ -274,6 +276,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RC_FF, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RC_FF|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_RC_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
|
|
@ -230,6 +230,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_ON, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_OFF, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
@ -561,6 +563,8 @@ static const struct button_mapping button_context_pitchscreen_nonlcdremote[] =
|
|||
{ ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT,BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
@ -577,6 +581,8 @@ static const struct button_mapping button_context_pitchscreen_h100lcdremote[] =
|
|||
{ ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_SOURCE|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_BITRATE|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
};
|
||||
|
@ -593,6 +599,8 @@ static const struct button_mapping button_context_pitchscreen_h300lcdremote[] =
|
|||
{ ACTION_PS_RESET, BUTTON_RC_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_ON, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_STOP, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT,BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
};
|
||||
|
|
|
@ -238,8 +238,10 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_VIEW, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_VIEW, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -197,6 +197,10 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
|
||||
{ ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_MENU, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
#endif
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
|
|
@ -132,6 +132,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_MODE, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -146,6 +146,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchscreen */
|
||||
|
|
|
@ -190,6 +190,9 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_MODE, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
||||
|
|
|
@ -207,6 +207,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_RESET, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
|
||||
}; /* button_context_pitchscreen */
|
||||
|
||||
|
|
|
@ -162,6 +162,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MODE, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_REC, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchscreen */
|
||||
|
@ -178,6 +180,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_RC_MODE, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_RC_REC, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* remote_button_context_pitchscreen */
|
||||
|
|
|
@ -227,6 +227,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -237,6 +237,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_DISPLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -240,6 +240,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_MENU, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_RIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_REW|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_FFWD|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
|
|
@ -206,6 +206,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_BOTTOMRIGHT, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_CENTER, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_TOPLEFT, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_MIDLEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_MIDRIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM2|CONTEXT_PITCHSCREEN)
|
||||
}; /* button_context_pitchcreen */
|
||||
|
||||
|
|
|
@ -151,6 +151,8 @@ static const struct button_mapping button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_TOGGLE_MODE, BUTTON_SELECT, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_POWER, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* button_context_pitchscreen */
|
||||
|
@ -166,6 +168,8 @@ static const struct button_mapping remote_button_context_pitchscreen[] = {
|
|||
{ ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RC_FF|BUTTON_REL, BUTTON_NONE },
|
||||
{ ACTION_PS_RESET, BUTTON_RC_MODE, BUTTON_NONE },
|
||||
{ ACTION_PS_EXIT, BUTTON_RC_PLAY, BUTTON_NONE },
|
||||
{ ACTION_PS_SLOWER, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE },
|
||||
{ ACTION_PS_FASTER, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE },
|
||||
|
||||
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
|
||||
}; /* remote_button_context_pitchscreen */
|
||||
|
|
|
@ -12468,3 +12468,37 @@
|
|||
*: "Prevent Track Skipping"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_TIMESTRETCH
|
||||
desc: timestretch enable
|
||||
user: core
|
||||
<source>
|
||||
*: none
|
||||
swcodec: "Timestretch"
|
||||
</source>
|
||||
<dest>
|
||||
*: none
|
||||
swcodec: "Timestretch"
|
||||
</dest>
|
||||
<voice>
|
||||
*: none
|
||||
swcodec: "Timestretch"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_SPEED
|
||||
desc: timestretch speed
|
||||
user: core
|
||||
<source>
|
||||
*: none
|
||||
swcodec: "Speed"
|
||||
</source>
|
||||
<dest>
|
||||
*: none
|
||||
swcodec: "Speed"
|
||||
</dest>
|
||||
<voice>
|
||||
*: none
|
||||
swcodec: "Speed"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
|
@ -32,6 +31,9 @@
|
|||
#include "eq_menu.h"
|
||||
#include "exported_menus.h"
|
||||
#include "menu_common.h"
|
||||
#include "splash.h"
|
||||
#include "kernel.h"
|
||||
#include "dsp.h"
|
||||
|
||||
/***********************************/
|
||||
/* SOUND MENU */
|
||||
|
@ -57,14 +59,14 @@ MENUITEM_SETTING(treble, &global_settings.treble,
|
|||
MENUITEM_SETTING(treble_cutoff, &global_settings.treble_cutoff, NULL);
|
||||
#endif
|
||||
MENUITEM_SETTING(balance, &global_settings.balance, NULL);
|
||||
MENUITEM_SETTING(channel_config, &global_settings.channel_config,
|
||||
MENUITEM_SETTING(channel_config, &global_settings.channel_config,
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
lowlatency_callback
|
||||
#else
|
||||
NULL
|
||||
#endif
|
||||
);
|
||||
MENUITEM_SETTING(stereo_width, &global_settings.stereo_width,
|
||||
MENUITEM_SETTING(stereo_width, &global_settings.stereo_width,
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
lowlatency_callback
|
||||
#else
|
||||
|
@ -86,7 +88,21 @@ MENUITEM_SETTING(stereo_width, &global_settings.stereo_width,
|
|||
MAKE_MENU(crossfeed_menu,ID2P(LANG_CROSSFEED), NULL, Icon_NOICON,
|
||||
&crossfeed, &crossfeed_direct_gain, &crossfeed_cross_gain,
|
||||
&crossfeed_hf_attenuation, &crossfeed_hf_cutoff);
|
||||
|
||||
|
||||
static int timestretch_callback(int action,const struct menu_item_ex *this_item)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ACTION_EXIT_MENUITEM: /* on exit */
|
||||
if (global_settings.timestretch_enabled && !dsp_timestretch_enabled())
|
||||
splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
|
||||
break;
|
||||
}
|
||||
lowlatency_callback(action, this_item);
|
||||
return action;
|
||||
}
|
||||
MENUITEM_SETTING(timestretch_enabled,
|
||||
&global_settings.timestretch_enabled, timestretch_callback);
|
||||
MENUITEM_SETTING(dithering_enabled,
|
||||
&global_settings.dithering_enabled, lowlatency_callback);
|
||||
#endif
|
||||
|
@ -120,7 +136,8 @@ MAKE_MENU(sound_settings, ID2P(LANG_SOUND_SETTINGS), NULL, Icon_Audio,
|
|||
#endif
|
||||
&balance,&channel_config,&stereo_width
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
,&crossfeed_menu, &equalizer_menu, &dithering_enabled
|
||||
,&crossfeed_menu, &equalizer_menu, &dithering_enabled
|
||||
,×tretch_enabled
|
||||
#endif
|
||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
|
||||
,&loudness,&avc,&superbass,&mdb_enable,&mdb_strength
|
||||
|
|
|
@ -936,6 +936,7 @@ void settings_apply(bool read_disk)
|
|||
}
|
||||
|
||||
dsp_dither_enable(global_settings.dithering_enabled);
|
||||
dsp_timestretch_enable(global_settings.timestretch_enabled);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SPDIF_POWER
|
||||
|
|
|
@ -374,6 +374,7 @@ struct user_settings
|
|||
int keyclick; /* keyclick volume */
|
||||
int keyclick_repeats; /* keyclick on repeats */
|
||||
bool dithering_enabled;
|
||||
bool timestretch_enabled;
|
||||
#endif /* CONFIG_CODEC == SWCODEC */
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
|
|
|
@ -1181,6 +1181,10 @@ const struct settings_list settings[] = {
|
|||
/* dithering */
|
||||
OFFON_SETTING(F_SOUNDSETTING, dithering_enabled, LANG_DITHERING, false,
|
||||
"dithering enabled", dsp_dither_enable),
|
||||
|
||||
/* timestretch */
|
||||
OFFON_SETTING(F_SOUNDSETTING, timestretch_enabled, LANG_TIMESTRETCH, false,
|
||||
"timestretch enabled", dsp_timestretch_enable),
|
||||
#endif
|
||||
#ifdef HAVE_WM8758
|
||||
SOUND_SETTING(F_NO_WRAP, bass_cutoff, LANG_BASS_CUTOFF,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _TDSPEED_H
|
||||
#define _TDSPEED_H
|
||||
|
||||
#define TDSPEED_OUTBUFSIZE 4096
|
||||
|
||||
bool tdspeed_init(int samplerate, bool stereo, int factor);
|
||||
long tdspeed_est_output_size(void);
|
||||
long tdspeed_est_input_size(long size);
|
||||
int tdspeed_doit(int32_t *src[], int count);
|
||||
|
||||
#define SPEED_MAX 250
|
||||
#define SPEED_MIN 35
|
||||
|
||||
#endif
|
|
@ -468,6 +468,8 @@ Michael Carr
|
|||
Eric Clayton
|
||||
Marko Pahlke
|
||||
Vytenis Sabelka
|
||||
Nicolas Pitre
|
||||
Benedikt Goos
|
||||
|
||||
The libmad team
|
||||
The wavpack team
|
||||
|
|
|
@ -421,3 +421,13 @@ and not easily noticable.
|
|||
Rockbox uses highpass triangular distribution noise as the dithering noise
|
||||
source, and a third order noise shaper.
|
||||
}
|
||||
|
||||
\opt{swcodec}{
|
||||
\section{Timestretch}
|
||||
Enabling \setting{Timestretch} allows you to change the playback speed without it
|
||||
affecting the pitch of the recording.
|
||||
|
||||
After enabling this feature and rebooting, you can access this via the \setting{Pitch Screen}.
|
||||
This function is intended for speech playback and may significantly dilute your listening
|
||||
experience with more complex audio.
|
||||
}
|
||||
|
|
|
@ -239,44 +239,78 @@ Delete the currently playing file.
|
|||
\nopt{player}{
|
||||
\subsubsection{\label{sec:pitchscreen}Pitch}
|
||||
|
||||
The \setting{Pitch Screen} allows you to change the pitch and (at the same
|
||||
time) the playback speed of your \dap. The pitch value can be adjusted
|
||||
between 50\% and 200\%. 50\% means half the normal playback speed and the
|
||||
pitch that is an octave lower than the normal pitch. 200\% means double
|
||||
playback speed and the pitch that is an octave higher than the normal pitch.
|
||||
It is not possible to change the pitch without changing the playback speed and
|
||||
vice versa. Changing the pitch can be done in two modes: procentual and
|
||||
semitone. Initially (after the \dap{} is switched on), procentual mode
|
||||
is active.
|
||||
|
||||
\begin{table}
|
||||
\begin{btnmap}{}{}
|
||||
\ActionPsToggleMode
|
||||
& Toggle pitch changing mode \\
|
||||
%
|
||||
\ActionPsIncSmall{} / \ActionPsDecSmall
|
||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
%
|
||||
\ActionPsIncBig{} / \ActionPsDecBig
|
||||
& Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
%
|
||||
\ActionPsNudgeRight{} / \ActionPsNudgeLeft
|
||||
& Temporarily increase / decrease pitch by 2.0\% \\
|
||||
%
|
||||
\ActionPsReset
|
||||
& Reset pitch to 100\% \\
|
||||
%
|
||||
\ActionPsExit
|
||||
& Leave the Pitch Screen \\
|
||||
%
|
||||
\end{btnmap}
|
||||
\end{table}
|
||||
The \setting{Pitch Screen} allows you to change the pitch and the playback
|
||||
speed of your \dap. The pitch value can be adjusted between 50\% and 200\%.
|
||||
50\% means half the normal playback speed and the pitch that is an octave lower
|
||||
than the normal pitch. 200\% means double playback speed and the pitch that
|
||||
is an octave higher than the normal pitch.
|
||||
|
||||
\opt{masf}{
|
||||
Changing the pitch can be done in two modes: procentual and semitone.
|
||||
Initially (after the \dap{} is switched on), procentual mode is active.
|
||||
|
||||
\begin{table}
|
||||
\begin{btnmap}{}{}
|
||||
\ActionPsToggleMode
|
||||
& Toggle pitch changing mode \\
|
||||
%
|
||||
\ActionPsIncSmall{} / \ActionPsDecSmall
|
||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
%
|
||||
\ActionPsIncBig{} / \ActionPsDecBig
|
||||
& Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
%
|
||||
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
||||
& Temporarily change pitch by 2.0\% (beatmatch) \\
|
||||
%
|
||||
\ActionPsReset
|
||||
& Reset pitch to 100\% \\
|
||||
%
|
||||
\ActionPsExit
|
||||
& Leave the Pitch Screen \\
|
||||
%
|
||||
\end{btnmap}
|
||||
\end{table}
|
||||
|
||||
\opt{MASCODEC}{
|
||||
\warn{Changing the pitch can cause audible 'Artifacts' or 'Dropouts'.}
|
||||
}
|
||||
|
||||
\opt{swcodec}{
|
||||
Changing the pitch can be done in three modes: procentual, semitone and
|
||||
timestretch. Initially (after the \dap{} is switched on), procentual mode is active.
|
||||
|
||||
Timestretch mode allows you to change the playback speed of your recording without
|
||||
affecting the pitch, and vice versa. To access this you must enable the \setting{Timestretch}
|
||||
option in \setting{Sound Settings} and reboot.
|
||||
|
||||
\begin{table}
|
||||
\begin{btnmap}{}{}
|
||||
\ActionPsToggleMode
|
||||
& Toggle pitch changing mode \\
|
||||
%
|
||||
\ActionPsIncSmall{} / \ActionPsDecSmall
|
||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
%
|
||||
\ActionPsIncBig{} / \ActionPsDecBig
|
||||
& Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
%
|
||||
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
||||
& Temporarily change pitch by 2.0\% (beatmatch), or modify speed (in timestretch mode) \\
|
||||
%
|
||||
\ActionPsReset
|
||||
& Reset pitch and speed to 100\% \\
|
||||
%
|
||||
\ActionPsExit
|
||||
& Leave the Pitch Screen \\
|
||||
%
|
||||
\end{btnmap}
|
||||
\end{table}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%********************QUICKSCREENS***********************************************
|
||||
|
|
Loading…
Reference in New Issue