forked from len0rd/rockbox
Improvements to the pitch screen UI (FS#10359 by David Johnston)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21781 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
17ac0d7ff9
commit
cc7c665d9b
14 changed files with 701 additions and 192 deletions
18
apps/dsp.c
18
apps/dsp.c
|
|
@ -162,7 +162,7 @@ struct dsp_config
|
|||
int sample_depth;
|
||||
int sample_bytes;
|
||||
int stereo_mode;
|
||||
int tdspeed_percent; /* Speed % */
|
||||
int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */
|
||||
bool tdspeed_active; /* Timestretch is in use */
|
||||
int frac_bits;
|
||||
#ifdef HAVE_SW_TONE_CONTROLS
|
||||
|
|
@ -205,7 +205,7 @@ static int treble; /* A/V */
|
|||
#endif
|
||||
|
||||
/* Settings applicable to audio codec only */
|
||||
static int pitch_ratio = 1000;
|
||||
static int32_t pitch_ratio = PITCH_SPEED_100;
|
||||
static int channels_mode;
|
||||
long dsp_sw_gain;
|
||||
long dsp_sw_cross;
|
||||
|
|
@ -254,14 +254,14 @@ static inline int32_t clip_sample_16(int32_t sample)
|
|||
return sample;
|
||||
}
|
||||
|
||||
int sound_get_pitch(void)
|
||||
int32_t sound_get_pitch(void)
|
||||
{
|
||||
return pitch_ratio;
|
||||
}
|
||||
|
||||
void sound_set_pitch(int permille)
|
||||
void sound_set_pitch(int32_t percent)
|
||||
{
|
||||
pitch_ratio = permille;
|
||||
pitch_ratio = percent;
|
||||
dsp_configure(&AUDIO_DSP, DSP_SWITCH_FREQUENCY,
|
||||
AUDIO_DSP.codec_frequency);
|
||||
}
|
||||
|
|
@ -277,7 +277,7 @@ static void tdspeed_setup(struct dsp_config *dspc)
|
|||
if(!dsp_timestretch_available())
|
||||
return; /* Timestretch not enabled or buffer not allocated */
|
||||
if (dspc->tdspeed_percent == 0)
|
||||
dspc->tdspeed_percent = 100;
|
||||
dspc->tdspeed_percent = PITCH_SPEED_100;
|
||||
if (!tdspeed_config(
|
||||
dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency,
|
||||
dspc->stereo_mode != STEREO_MONO,
|
||||
|
|
@ -312,13 +312,13 @@ void dsp_timestretch_enable(bool enabled)
|
|||
}
|
||||
}
|
||||
|
||||
void dsp_set_timestretch(int percent)
|
||||
void dsp_set_timestretch(int32_t percent)
|
||||
{
|
||||
AUDIO_DSP.tdspeed_percent = percent;
|
||||
tdspeed_setup(&AUDIO_DSP);
|
||||
}
|
||||
|
||||
int dsp_get_timestretch()
|
||||
int32_t dsp_get_timestretch()
|
||||
{
|
||||
return AUDIO_DSP.tdspeed_percent;
|
||||
}
|
||||
|
|
@ -1347,7 +1347,7 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
|
|||
not need this feature.
|
||||
*/
|
||||
if (dsp == &AUDIO_DSP)
|
||||
dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000;
|
||||
dsp->frequency = pitch_ratio * dsp->codec_frequency / PITCH_SPEED_100;
|
||||
else
|
||||
dsp->frequency = dsp->codec_frequency;
|
||||
|
||||
|
|
|
|||
|
|
@ -83,10 +83,10 @@ void dsp_set_eq_coefs(int band);
|
|||
void dsp_dither_enable(bool enable);
|
||||
void dsp_timestretch_enable(bool enable);
|
||||
bool dsp_timestretch_available(void);
|
||||
void sound_set_pitch(int r);
|
||||
int sound_get_pitch(void);
|
||||
void dsp_set_timestretch(int percent);
|
||||
int dsp_get_timestretch(void);
|
||||
void sound_set_pitch(int32_t r);
|
||||
int32_t sound_get_pitch(void);
|
||||
void dsp_set_timestretch(int32_t percent);
|
||||
int32_t dsp_get_timestretch(void);
|
||||
int dsp_callback(int msg, intptr_t param);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "config.h"
|
||||
#include "sprintf.h"
|
||||
#include "action.h"
|
||||
|
|
@ -36,24 +37,27 @@
|
|||
#include "system.h"
|
||||
#include "misc.h"
|
||||
#include "pitchscreen.h"
|
||||
#include "settings.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "tdspeed.h"
|
||||
#endif
|
||||
|
||||
#define ABS(x) ((x) > 0 ? (x) : -(x))
|
||||
|
||||
#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
|
||||
#define PITCH_SMALL_DELTA 1
|
||||
#define PITCH_BIG_DELTA 10
|
||||
#define PITCH_NUDGE_DELTA 20
|
||||
#define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
|
||||
#define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
|
||||
#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
|
||||
#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
|
||||
#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
|
||||
|
||||
static bool pitch_mode_semitone = false;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
static bool pitch_mode_timestretch = false;
|
||||
#endif
|
||||
#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
|
||||
#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
|
||||
|
||||
#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
|
||||
#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
@ -63,25 +67,111 @@ enum
|
|||
PITCH_ITEM_COUNT,
|
||||
};
|
||||
|
||||
|
||||
/* This is a table of semitone percentage values of the appropriate
|
||||
precision (based on PITCH_SPEED_PRECISION). Note that these are
|
||||
all constant expressions, which will be evaluated at compile time,
|
||||
so no need to worry about how complex the expressions look.
|
||||
That's just to get the precision right.
|
||||
|
||||
I calculated these values, starting from 50, as
|
||||
|
||||
x(n) = 50 * 2^(n/12)
|
||||
|
||||
All that math in each entry simply converts the float constant
|
||||
to an integer equal to PITCH_SPEED_PRECISION times the float value,
|
||||
with as little precision loss as possible.
|
||||
*/
|
||||
#define SEMITONE_VALUE(x) \
|
||||
( (int)(((x) + 0.5 / PITCH_SPEED_PRECISION) * PITCH_SPEED_PRECISION) )
|
||||
|
||||
static const int semitone_table[] =
|
||||
{
|
||||
SEMITONE_VALUE(50),
|
||||
SEMITONE_VALUE(52.97315472),
|
||||
SEMITONE_VALUE(56.12310242),
|
||||
SEMITONE_VALUE(59.46035575),
|
||||
SEMITONE_VALUE(62.99605249),
|
||||
SEMITONE_VALUE(66.74199271),
|
||||
SEMITONE_VALUE(70.71067812),
|
||||
SEMITONE_VALUE(74.91535384),
|
||||
SEMITONE_VALUE(79.3700526 ),
|
||||
SEMITONE_VALUE(84.08964153),
|
||||
SEMITONE_VALUE(89.08987181),
|
||||
SEMITONE_VALUE(94.38743127),
|
||||
SEMITONE_VALUE(100 ),
|
||||
SEMITONE_VALUE(105.9463094),
|
||||
SEMITONE_VALUE(112.2462048),
|
||||
SEMITONE_VALUE(118.9207115),
|
||||
SEMITONE_VALUE(125.992105 ),
|
||||
SEMITONE_VALUE(133.4839854),
|
||||
SEMITONE_VALUE(141.4213562),
|
||||
SEMITONE_VALUE(149.8307077),
|
||||
SEMITONE_VALUE(158.7401052),
|
||||
SEMITONE_VALUE(168.1792831),
|
||||
SEMITONE_VALUE(178.1797436),
|
||||
SEMITONE_VALUE(188.7748625),
|
||||
SEMITONE_VALUE(200 )
|
||||
};
|
||||
|
||||
#define NUM_SEMITONES ((int)(sizeof(semitone_table) / sizeof(int)))
|
||||
#define SEMITONE_START -12
|
||||
#define SEMITONE_END 12
|
||||
|
||||
/* A table of values for approximating the cent curve with
|
||||
linear interpolation. Multipy the next lowest semitone
|
||||
by this much to find the corresponding cent percentage.
|
||||
|
||||
These values were calculated as
|
||||
x(n) = 100 * 2^(n * 20/1200)
|
||||
*/
|
||||
|
||||
#define CENT_INTERP(x) \
|
||||
( (int)(((x) + 0.5 / PITCH_SPEED_PRECISION) * PITCH_SPEED_PRECISION) )
|
||||
|
||||
|
||||
static const int cent_interp[] =
|
||||
{
|
||||
PITCH_SPEED_100,
|
||||
CENT_INTERP(101.1619440),
|
||||
CENT_INTERP(102.3373892),
|
||||
CENT_INTERP(103.5264924),
|
||||
CENT_INTERP(104.7294123),
|
||||
/* this one's the next semitone but we have it here for convenience */
|
||||
CENT_INTERP(105.9463094),
|
||||
};
|
||||
|
||||
/* Number of cents between entries in the cent_interp table */
|
||||
#define CENT_INTERP_INTERVAL 20
|
||||
#define CENT_INTERP_NUM ((int)(sizeof(cent_interp)/sizeof(int)))
|
||||
|
||||
/* This stores whether the pitch and speed are at their own limits */
|
||||
/* or that of the timestretching algorithm */
|
||||
static bool at_limit = false;
|
||||
|
||||
static void pitchscreen_fix_viewports(struct viewport *parent,
|
||||
struct viewport pitch_viewports[PITCH_ITEM_COUNT])
|
||||
{
|
||||
int i, height;
|
||||
height = font_get(parent->font)->height;
|
||||
int i, font_height;
|
||||
font_height = font_get(parent->font)->height;
|
||||
for (i = 0; i < PITCH_ITEM_COUNT; i++)
|
||||
{
|
||||
pitch_viewports[i] = *parent;
|
||||
pitch_viewports[i].height = height;
|
||||
pitch_viewports[i].height = font_height;
|
||||
}
|
||||
pitch_viewports[PITCH_TOP].y += ICON_BORDER;
|
||||
|
||||
pitch_viewports[PITCH_MID].x += ICON_BORDER;
|
||||
pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
|
||||
pitch_viewports[PITCH_MID].height = height * 2;
|
||||
pitch_viewports[PITCH_MID].height = parent->height - ICON_BORDER*2
|
||||
- font_height * 2;
|
||||
if(pitch_viewports[PITCH_MID].height < font_height * 2)
|
||||
pitch_viewports[PITCH_MID].height = font_height * 2;
|
||||
pitch_viewports[PITCH_MID].y += parent->height / 2 -
|
||||
pitch_viewports[PITCH_MID].height / 2;
|
||||
|
||||
pitch_viewports[PITCH_BOTTOM].y += parent->height - height - ICON_BORDER;
|
||||
pitch_viewports[PITCH_BOTTOM].y += parent->height - font_height
|
||||
- ICON_BORDER;
|
||||
}
|
||||
|
||||
/* must be called before pitchscreen_draw, or within
|
||||
|
|
@ -107,9 +197,9 @@ static void pitchscreen_draw_icons(struct screen *display,
|
|||
|
||||
static void pitchscreen_draw(struct screen *display, int max_lines,
|
||||
struct viewport pitch_viewports[PITCH_ITEM_COUNT],
|
||||
int pitch
|
||||
int32_t pitch, int32_t semitone
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
,int speed
|
||||
,int32_t speed
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
|
@ -123,7 +213,7 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
|||
{
|
||||
/* UP: Pitch Up */
|
||||
display->set_viewport(&pitch_viewports[PITCH_TOP]);
|
||||
if (pitch_mode_semitone)
|
||||
if (global_settings.pitch_mode_semitone)
|
||||
ptr = str(LANG_PITCH_UP_SEMITONE);
|
||||
else
|
||||
ptr = str(LANG_PITCH_UP);
|
||||
|
|
@ -136,7 +226,7 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
|||
|
||||
/* DOWN: Pitch Down */
|
||||
display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
|
||||
if (pitch_mode_semitone)
|
||||
if (global_settings.pitch_mode_semitone)
|
||||
ptr = str(LANG_PITCH_DOWN_SEMITONE);
|
||||
else
|
||||
ptr = str(LANG_PITCH_DOWN);
|
||||
|
|
@ -157,55 +247,95 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
|||
if ((show_lang_pitch = (max_lines >= 3)))
|
||||
{
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (!pitch_mode_timestretch)
|
||||
if(global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
#endif
|
||||
/* LANG_PITCH */
|
||||
snprintf(buf, sizeof(buf), "%s", str(LANG_PITCH));
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* Pitch:XXX.X% */
|
||||
if(global_settings.pitch_mode_semitone)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s: %s%ld.%02ld", str(LANG_PITCH),
|
||||
semitone >= 0 ? "+" : "-",
|
||||
ABS(semitone / PITCH_SPEED_PRECISION),
|
||||
ABS((semitone % PITCH_SPEED_PRECISION) /
|
||||
(PITCH_SPEED_PRECISION / 100))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Pitch:XXX.X% */
|
||||
snprintf(buf, sizeof(buf), "%s:%d.%d%%", str(LANG_PITCH),
|
||||
pitch / 10, pitch % 10);
|
||||
snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
|
||||
pitch / PITCH_SPEED_PRECISION,
|
||||
(pitch % PITCH_SPEED_PRECISION) /
|
||||
(PITCH_SPEED_PRECISION / 10));
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Rate */
|
||||
snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
|
||||
}
|
||||
display->getstringsize(buf, &w, &h);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||
0, buf);
|
||||
(pitch_viewports[PITCH_MID].height / 2) - h, buf);
|
||||
if (w > width_used)
|
||||
width_used = w;
|
||||
}
|
||||
|
||||
/* Middle section lower line */
|
||||
/* "Speed:XXX%" */
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (!pitch_mode_timestretch)
|
||||
if(global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
|
||||
speed / PITCH_SPEED_PRECISION,
|
||||
(speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* "XXX.X%" */
|
||||
snprintf(buf, sizeof(buf), "%d.%d%%",
|
||||
pitch / 10, pitch % 10);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
{
|
||||
if(global_settings.pitch_mode_semitone)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s%ld.%02ld",
|
||||
semitone >= 0 ? "+" : "-",
|
||||
ABS(semitone / PITCH_SPEED_PRECISION),
|
||||
ABS((semitone % PITCH_SPEED_PRECISION) /
|
||||
(PITCH_SPEED_PRECISION / 100))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* "Speed:XXX%" */
|
||||
snprintf(buf, sizeof(buf), "%s:%d%%", str(LANG_SPEED),
|
||||
speed / 1000);
|
||||
snprintf(buf, sizeof(buf), "%ld.%ld%%",
|
||||
pitch / PITCH_SPEED_PRECISION,
|
||||
(pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
display->getstringsize(buf, &w, &h);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||
(show_lang_pitch ? h : h/2), buf);
|
||||
show_lang_pitch ? (pitch_viewports[PITCH_MID].height / 2) :
|
||||
(pitch_viewports[PITCH_MID].height / 2) - (h / 2),
|
||||
buf);
|
||||
if (w > width_used)
|
||||
width_used = w;
|
||||
|
||||
/* "limit" and "timestretch" labels */
|
||||
if (max_lines >= 7)
|
||||
{
|
||||
if(at_limit)
|
||||
{
|
||||
snprintf(buf, sizeof(buf), "%s", str(LANG_STRETCH_LIMIT));
|
||||
display->getstringsize(buf, &w, &h);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||
(pitch_viewports[PITCH_MID].height / 2) + h, 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_timestretch)
|
||||
if (global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
leftlabel = "<<";
|
||||
rightlabel = ">>";
|
||||
|
|
@ -220,37 +350,67 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
|||
|
||||
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->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
|
||||
leftlabel);
|
||||
display->putsxy((pitch_viewports[PITCH_MID].width - w),
|
||||
(pitch_viewports[PITCH_MID].height / 2) - (h / 2),
|
||||
rightlabel);
|
||||
}
|
||||
display->update_viewport();
|
||||
display->set_viewport(NULL);
|
||||
}
|
||||
|
||||
static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
|
||||
static int32_t pitch_increase(int32_t pitch, int32_t pitch_delta, bool allow_cutoff
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* need this to maintain correct pitch/speed caps */
|
||||
, int32_t speed
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int new_pitch;
|
||||
int32_t new_pitch;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
int32_t new_stretch;
|
||||
#endif
|
||||
at_limit = false;
|
||||
|
||||
if (pitch_delta < 0)
|
||||
{
|
||||
if (pitch + pitch_delta >= PITCH_MIN)
|
||||
/* for large jumps, snap up to whole numbers */
|
||||
if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
|
||||
(pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
|
||||
{
|
||||
pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
|
||||
}
|
||||
|
||||
new_pitch = pitch + pitch_delta;
|
||||
else
|
||||
|
||||
if (new_pitch < PITCH_MIN)
|
||||
{
|
||||
if (!allow_cutoff)
|
||||
{
|
||||
return pitch;
|
||||
}
|
||||
new_pitch = PITCH_MIN;
|
||||
at_limit = true;
|
||||
}
|
||||
}
|
||||
else if (pitch_delta > 0)
|
||||
{
|
||||
if (pitch + pitch_delta <= PITCH_MAX)
|
||||
/* for large jumps, snap down to whole numbers */
|
||||
if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
|
||||
(pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
|
||||
{
|
||||
pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
|
||||
}
|
||||
|
||||
new_pitch = pitch + pitch_delta;
|
||||
else
|
||||
|
||||
if (new_pitch > PITCH_MAX)
|
||||
{
|
||||
if (!allow_cutoff)
|
||||
return pitch;
|
||||
new_pitch = PITCH_MAX;
|
||||
at_limit = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -258,47 +418,164 @@ static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
|
|||
/* pitch_delta == 0 -> no real change */
|
||||
return pitch;
|
||||
}
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (dsp_timestretch_available())
|
||||
{
|
||||
/* increase the multiple to increase precision of this calculation */
|
||||
new_stretch = GET_STRETCH(new_pitch, speed);
|
||||
if(new_stretch < STRETCH_MIN)
|
||||
{
|
||||
/* we have to ignore allow_cutoff, because we can't have the */
|
||||
/* stretch go higher than STRETCH_MAX */
|
||||
new_pitch = GET_PITCH(speed, STRETCH_MIN);
|
||||
}
|
||||
else if(new_stretch > STRETCH_MAX)
|
||||
{
|
||||
/* we have to ignore allow_cutoff, because we can't have the */
|
||||
/* stretch go higher than STRETCH_MAX */
|
||||
new_pitch = GET_PITCH(speed, STRETCH_MAX);
|
||||
}
|
||||
|
||||
if(new_stretch >= STRETCH_MAX ||
|
||||
new_stretch <= STRETCH_MIN)
|
||||
{
|
||||
at_limit = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sound_set_pitch(new_pitch);
|
||||
|
||||
return new_pitch;
|
||||
}
|
||||
|
||||
/* Factor for changing the pitch one half tone up.
|
||||
The exact value is 2^(1/12) = 1.05946309436
|
||||
But we use only integer arithmetics, so take
|
||||
rounded factor multiplied by 10^5=100,000. This is
|
||||
enough to get the same promille values as if we
|
||||
had used floating point (checked with a spread
|
||||
sheet).
|
||||
*/
|
||||
#define PITCH_SEMITONE_FACTOR 105946L
|
||||
|
||||
/* Some helpful constants. K is the scaling factor for SEMITONE.
|
||||
N is for more accurate rounding
|
||||
KN is K * N
|
||||
*/
|
||||
#define PITCH_K_FCT 100000UL
|
||||
#define PITCH_N_FCT 10
|
||||
#define PITCH_KN_FCT 1000000UL
|
||||
|
||||
static int pitch_increase_semitone(int pitch, bool up)
|
||||
static int32_t get_semitone_from_pitch(int32_t pitch)
|
||||
{
|
||||
uint32_t tmp;
|
||||
uint32_t round_fct; /* How much to scale down at the end */
|
||||
tmp = pitch;
|
||||
if (up)
|
||||
int semitone = 0;
|
||||
int32_t fractional_index = 0;
|
||||
|
||||
while(semitone < NUM_SEMITONES - 1 &&
|
||||
pitch >= semitone_table[semitone + 1])
|
||||
{
|
||||
tmp = tmp * PITCH_SEMITONE_FACTOR;
|
||||
round_fct = PITCH_K_FCT;
|
||||
semitone++;
|
||||
}
|
||||
|
||||
|
||||
/* now find the fractional part */
|
||||
while(pitch > (cent_interp[fractional_index + 1] *
|
||||
semitone_table[semitone] / PITCH_SPEED_100))
|
||||
{
|
||||
/* Check to make sure fractional_index isn't too big */
|
||||
/* This should never happen. */
|
||||
if(fractional_index >= CENT_INTERP_NUM - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
fractional_index++;
|
||||
}
|
||||
|
||||
int32_t semitone_pitch_a = cent_interp[fractional_index] *
|
||||
semitone_table[semitone] /
|
||||
PITCH_SPEED_100;
|
||||
int32_t semitone_pitch_b = cent_interp[fractional_index + 1] *
|
||||
semitone_table[semitone] /
|
||||
PITCH_SPEED_100;
|
||||
/* this will be the integer offset from the cent_interp entry */
|
||||
int32_t semitone_frac_ofs = (pitch - semitone_pitch_a) * CENT_INTERP_INTERVAL /
|
||||
(semitone_pitch_b - semitone_pitch_a);
|
||||
semitone = (semitone + SEMITONE_START) * PITCH_SPEED_PRECISION +
|
||||
fractional_index * CENT_INTERP_INTERVAL +
|
||||
semitone_frac_ofs;
|
||||
|
||||
return semitone;
|
||||
}
|
||||
|
||||
static int32_t get_pitch_from_semitone(int32_t semitone)
|
||||
{
|
||||
int32_t adjusted_semitone = semitone - SEMITONE_START * PITCH_SPEED_PRECISION;
|
||||
|
||||
/* Find the index into the semitone table */
|
||||
int32_t semitone_index = (adjusted_semitone / PITCH_SPEED_PRECISION);
|
||||
|
||||
/* set pitch to the semitone's integer part value */
|
||||
int32_t pitch = semitone_table[semitone_index];
|
||||
/* get the range of the cent modification for future calculation */
|
||||
int32_t pitch_mod_a =
|
||||
cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
|
||||
CENT_INTERP_INTERVAL];
|
||||
int32_t pitch_mod_b =
|
||||
cent_interp[(adjusted_semitone % PITCH_SPEED_PRECISION) /
|
||||
CENT_INTERP_INTERVAL + 1];
|
||||
/* figure out the cent mod amount based on the semitone fractional value */
|
||||
int32_t pitch_mod = pitch_mod_a + (pitch_mod_b - pitch_mod_a) *
|
||||
(adjusted_semitone % CENT_INTERP_INTERVAL) / CENT_INTERP_INTERVAL;
|
||||
|
||||
/* modify pitch based on the mod amount we just calculated */
|
||||
return (pitch * pitch_mod + PITCH_SPEED_100 / 2) / PITCH_SPEED_100;
|
||||
}
|
||||
|
||||
static int32_t pitch_increase_semitone(int32_t pitch,
|
||||
int32_t current_semitone,
|
||||
int32_t semitone_delta
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, int32_t speed
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int32_t new_semitone = current_semitone;
|
||||
|
||||
/* snap to the delta interval */
|
||||
if(current_semitone % semitone_delta != 0)
|
||||
{
|
||||
if(current_semitone > 0 && semitone_delta > 0)
|
||||
new_semitone += semitone_delta;
|
||||
else if(current_semitone < 0 && semitone_delta < 0)
|
||||
new_semitone += semitone_delta;
|
||||
|
||||
new_semitone -= new_semitone % semitone_delta;
|
||||
}
|
||||
else
|
||||
new_semitone += semitone_delta;
|
||||
|
||||
/* clamp the pitch so it doesn't go beyond the pitch limits */
|
||||
if(new_semitone < (SEMITONE_START * PITCH_SPEED_PRECISION))
|
||||
{
|
||||
tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR;
|
||||
round_fct = PITCH_N_FCT;
|
||||
new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
|
||||
at_limit = true;
|
||||
}
|
||||
/* Scaling down with rounding */
|
||||
tmp = (tmp + round_fct / 2) / round_fct;
|
||||
return pitch_increase(pitch, tmp - pitch, false);
|
||||
else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
|
||||
{
|
||||
new_semitone = SEMITONE_END * PITCH_SPEED_PRECISION;
|
||||
at_limit = true;
|
||||
}
|
||||
|
||||
int32_t new_pitch = get_pitch_from_semitone(new_semitone);
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
int32_t new_stretch = GET_STRETCH(new_pitch, speed);
|
||||
|
||||
/* clamp the pitch so it doesn't go beyond the stretch limits */
|
||||
if( new_stretch > STRETCH_MAX)
|
||||
{
|
||||
new_pitch = GET_PITCH(speed, STRETCH_MAX);
|
||||
new_semitone = get_semitone_from_pitch(new_pitch);
|
||||
at_limit = true;
|
||||
}
|
||||
else if (new_stretch < STRETCH_MIN)
|
||||
{
|
||||
new_pitch = GET_PITCH(speed, STRETCH_MIN);
|
||||
new_semitone = get_semitone_from_pitch(new_pitch);
|
||||
at_limit = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
pitch_increase(pitch, new_pitch - pitch, false
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
|
||||
return new_semitone;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -310,13 +587,11 @@ static int pitch_increase_semitone(int pitch, bool up)
|
|||
int gui_syncpitchscreen_run(void)
|
||||
{
|
||||
int button, i;
|
||||
int pitch = sound_get_pitch();
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
int stretch = dsp_get_timestretch();
|
||||
int speed = stretch * pitch; /* speed to maintain */
|
||||
#endif
|
||||
int new_pitch;
|
||||
int pitch_delta;
|
||||
int32_t pitch = sound_get_pitch();
|
||||
int32_t semitone;
|
||||
|
||||
int32_t new_pitch;
|
||||
int32_t pitch_delta;
|
||||
bool nudged = false;
|
||||
bool exit = false;
|
||||
/* should maybe be passed per parameter later, not needed for now */
|
||||
|
|
@ -324,6 +599,31 @@ int gui_syncpitchscreen_run(void)
|
|||
struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
|
||||
int max_lines[NB_SCREENS];
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
int32_t new_speed = 0, new_stretch;
|
||||
|
||||
/* the speed variable holds the apparent speed of the playback */
|
||||
int32_t speed;
|
||||
if (dsp_timestretch_available())
|
||||
{
|
||||
speed = GET_SPEED(pitch, dsp_get_timestretch());
|
||||
}
|
||||
else
|
||||
{
|
||||
speed = pitch;
|
||||
}
|
||||
|
||||
/* Figure out whether to be in timestretch mode */
|
||||
if (global_settings.pitch_mode_timestretch && !dsp_timestretch_available())
|
||||
{
|
||||
global_settings.pitch_mode_timestretch = false;
|
||||
settings_save();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set the semitone index based on the current pitch */
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
|
||||
/* initialize pitchscreen vps */
|
||||
FOR_NB_SCREENS(i)
|
||||
{
|
||||
|
|
@ -343,49 +643,80 @@ int gui_syncpitchscreen_run(void)
|
|||
{
|
||||
FOR_NB_SCREENS(i)
|
||||
pitchscreen_draw(&screens[i], max_lines[i],
|
||||
pitch_viewports[i], pitch
|
||||
pitch_viewports[i], pitch, semitone
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
pitch_delta = 0;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
new_speed = 0;
|
||||
#endif
|
||||
button = get_action(CONTEXT_PITCHSCREEN, HZ);
|
||||
switch (button)
|
||||
{
|
||||
case ACTION_PS_INC_SMALL:
|
||||
if(global_settings.pitch_mode_semitone)
|
||||
pitch_delta = SEMITONE_SMALL_DELTA;
|
||||
else
|
||||
pitch_delta = PITCH_SMALL_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_INC_BIG:
|
||||
if(global_settings.pitch_mode_semitone)
|
||||
pitch_delta = SEMITONE_BIG_DELTA;
|
||||
else
|
||||
pitch_delta = PITCH_BIG_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_DEC_SMALL:
|
||||
if(global_settings.pitch_mode_semitone)
|
||||
pitch_delta = -SEMITONE_SMALL_DELTA;
|
||||
else
|
||||
pitch_delta = -PITCH_SMALL_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_DEC_BIG:
|
||||
if(global_settings.pitch_mode_semitone)
|
||||
pitch_delta = -SEMITONE_BIG_DELTA;
|
||||
else
|
||||
pitch_delta = -PITCH_BIG_DELTA;
|
||||
break;
|
||||
|
||||
case ACTION_PS_NUDGE_RIGHT:
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (!pitch_mode_timestretch)
|
||||
if (!global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
#endif
|
||||
new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
|
||||
new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
nudged = (new_pitch != pitch);
|
||||
pitch = new_pitch;
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
speed = pitch;
|
||||
#endif
|
||||
break;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
}
|
||||
else
|
||||
{
|
||||
new_speed = speed + SPEED_SMALL_DELTA;
|
||||
at_limit = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACTION_PS_FASTER:
|
||||
if (pitch_mode_timestretch && stretch < STRETCH_MAX)
|
||||
if (global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
stretch++;
|
||||
dsp_set_timestretch(stretch);
|
||||
speed = stretch * pitch;
|
||||
new_speed = speed + SPEED_BIG_DELTA;
|
||||
/* snap to whole numbers */
|
||||
if(new_speed % PITCH_SPEED_PRECISION != 0)
|
||||
new_speed -= new_speed % PITCH_SPEED_PRECISION;
|
||||
at_limit = false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
|
@ -393,29 +724,53 @@ int gui_syncpitchscreen_run(void)
|
|||
case ACTION_PS_NUDGE_RIGHTOFF:
|
||||
if (nudged)
|
||||
{
|
||||
pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
|
||||
pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
speed = pitch;
|
||||
#endif
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
nudged = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACTION_PS_NUDGE_LEFT:
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (!pitch_mode_timestretch)
|
||||
if (!global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
#endif
|
||||
new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
|
||||
new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
nudged = (new_pitch != pitch);
|
||||
pitch = new_pitch;
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
speed = pitch;
|
||||
#endif
|
||||
break;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
}
|
||||
else
|
||||
{
|
||||
new_speed = speed - SPEED_SMALL_DELTA;
|
||||
at_limit = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACTION_PS_SLOWER:
|
||||
if (pitch_mode_timestretch && stretch > STRETCH_MIN)
|
||||
if (global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
stretch--;
|
||||
dsp_set_timestretch(stretch);
|
||||
speed = stretch * pitch;
|
||||
new_speed = speed - SPEED_BIG_DELTA;
|
||||
/* snap to whole numbers */
|
||||
if(new_speed % PITCH_SPEED_PRECISION != 0)
|
||||
new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
|
||||
at_limit = false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
|
@ -423,27 +778,49 @@ int gui_syncpitchscreen_run(void)
|
|||
case ACTION_PS_NUDGE_LEFTOFF:
|
||||
if (nudged)
|
||||
{
|
||||
pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
|
||||
pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
speed = pitch;
|
||||
#endif
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
nudged = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACTION_PS_RESET:
|
||||
pitch = 1000;
|
||||
pitch = PITCH_SPEED_100;
|
||||
sound_set_pitch(pitch);
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
stretch = 100;
|
||||
dsp_set_timestretch(stretch);
|
||||
speed = stretch * pitch;
|
||||
speed = PITCH_SPEED_100;
|
||||
if (dsp_timestretch_available())
|
||||
{
|
||||
dsp_set_timestretch(PITCH_SPEED_100);
|
||||
at_limit = false;
|
||||
}
|
||||
#endif
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
break;
|
||||
|
||||
case ACTION_PS_TOGGLE_MODE:
|
||||
global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (dsp_timestretch_available() && pitch_mode_semitone)
|
||||
pitch_mode_timestretch = !pitch_mode_timestretch;
|
||||
|
||||
if (dsp_timestretch_available() && !global_settings.pitch_mode_semitone)
|
||||
{
|
||||
global_settings.pitch_mode_timestretch = !global_settings.pitch_mode_timestretch;
|
||||
if(!global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
/* no longer in timestretch mode. Reset speed */
|
||||
speed = pitch;
|
||||
dsp_set_timestretch(PITCH_SPEED_100);
|
||||
}
|
||||
}
|
||||
settings_save();
|
||||
#endif
|
||||
pitch_mode_semitone = !pitch_mode_semitone;
|
||||
break;
|
||||
|
||||
case ACTION_PS_EXIT:
|
||||
|
|
@ -457,27 +834,73 @@ int gui_syncpitchscreen_run(void)
|
|||
}
|
||||
if (pitch_delta)
|
||||
{
|
||||
if (pitch_mode_semitone)
|
||||
pitch = pitch_increase_semitone(pitch, pitch_delta > 0);
|
||||
else
|
||||
pitch = pitch_increase(pitch, pitch_delta, true);
|
||||
if (global_settings.pitch_mode_semitone)
|
||||
{
|
||||
semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (pitch_mode_timestretch)
|
||||
{
|
||||
/* Set stretch to maintain speed */
|
||||
/* i.e. increase pitch, reduce stretch */
|
||||
int new_stretch = speed / pitch;
|
||||
if (new_stretch >= STRETCH_MIN && new_stretch <= STRETCH_MAX)
|
||||
{
|
||||
stretch = new_stretch;
|
||||
dsp_set_timestretch(stretch);
|
||||
}
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
pitch = get_pitch_from_semitone(semitone);
|
||||
}
|
||||
else
|
||||
speed = stretch * pitch;
|
||||
{
|
||||
pitch = pitch_increase(pitch, pitch_delta, true
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
, speed
|
||||
#endif
|
||||
);
|
||||
semitone = get_semitone_from_pitch(pitch);
|
||||
}
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if (global_settings.pitch_mode_timestretch)
|
||||
{
|
||||
/* do this to make sure we properly obey the stretch limits */
|
||||
new_speed = speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
speed = pitch;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
if(new_speed)
|
||||
{
|
||||
new_stretch = GET_STRETCH(pitch, new_speed);
|
||||
|
||||
/* limit the amount of stretch */
|
||||
if(new_stretch > STRETCH_MAX)
|
||||
{
|
||||
new_stretch = STRETCH_MAX;
|
||||
new_speed = GET_SPEED(pitch, new_stretch);
|
||||
}
|
||||
else if(new_stretch < STRETCH_MIN)
|
||||
{
|
||||
new_stretch = STRETCH_MIN;
|
||||
new_speed = GET_SPEED(pitch, new_stretch);
|
||||
}
|
||||
|
||||
new_stretch = GET_STRETCH(pitch, new_speed);
|
||||
if(new_stretch >= STRETCH_MAX ||
|
||||
new_stretch <= STRETCH_MIN)
|
||||
{
|
||||
at_limit = true;
|
||||
}
|
||||
|
||||
/* set the amount of stretch */
|
||||
dsp_set_timestretch(new_stretch);
|
||||
|
||||
/* update the speed variable with the new speed */
|
||||
speed = new_speed;
|
||||
|
||||
/* Reset new_speed so we only call dsp_set_timestretch */
|
||||
/* when needed */
|
||||
new_speed = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
pcmbuf_set_low_latency(false);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@
|
|||
#ifndef _PITCHSCREEN_H_
|
||||
#define _PITCHSCREEN_H_
|
||||
|
||||
/* precision of the pitch and speed variables */
|
||||
/* One zero per decimal (100 means two decimal places */
|
||||
#define PITCH_SPEED_PRECISION 100L
|
||||
#define PITCH_SPEED_100 (100L * PITCH_SPEED_PRECISION) /* 100% speed */
|
||||
|
||||
int gui_syncpitchscreen_run(void);
|
||||
|
||||
#endif /* _PITCHSCREEN_H_ */
|
||||
|
|
|
|||
|
|
@ -12604,3 +12604,45 @@
|
|||
remote: "Remote Statusbar"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_SEMITONE
|
||||
desc:
|
||||
user: core
|
||||
<source>
|
||||
*: "Semitone"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Semitone"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Semitone"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_STRETCH_LIMIT
|
||||
desc: "limit" in pitch screen
|
||||
user: core
|
||||
<source>
|
||||
*: "Limit"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Limit"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Limit"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_PLAYBACK_RATE
|
||||
desc: "rate" in pitch screen
|
||||
user: core
|
||||
<source>
|
||||
*: "Rate"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Rate"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Rate"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
|||
|
|
@ -128,12 +128,12 @@ void* plugin_get_buffer(size_t *buffer_size);
|
|||
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||
|
||||
/* increase this every time the api struct changes */
|
||||
#define PLUGIN_API_VERSION 159
|
||||
#define PLUGIN_API_VERSION 160
|
||||
|
||||
/* update this to latest version if a change to the api struct breaks
|
||||
backwards compatibility (and please take the opportunity to sort in any
|
||||
new function which are "waiting" at the end of the function table) */
|
||||
#define PLUGIN_MIN_API_VERSION 159
|
||||
#define PLUGIN_MIN_API_VERSION 160
|
||||
|
||||
/* plugin return codes */
|
||||
enum plugin_status {
|
||||
|
|
@ -618,7 +618,7 @@ struct plugin_api {
|
|||
#endif
|
||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) || \
|
||||
(CONFIG_CODEC == SWCODEC)
|
||||
void (*sound_set_pitch)(int pitch);
|
||||
void (*sound_set_pitch)(int32_t pitch);
|
||||
#endif
|
||||
|
||||
/* MAS communication */
|
||||
|
|
|
|||
|
|
@ -739,6 +739,11 @@ struct user_settings
|
|||
struct touchscreen_parameter ts_calibration_data;
|
||||
#endif
|
||||
|
||||
/* pitch screen settings */
|
||||
bool pitch_mode_semitone;
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
bool pitch_mode_timestretch;
|
||||
#endif
|
||||
/* If values are just added to the end, no need to bump plugin API
|
||||
version. */
|
||||
/* new stuff to be added at the end */
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
#include "settings_list.h"
|
||||
#include "sound.h"
|
||||
#include "dsp.h"
|
||||
#include "debug.h"
|
||||
#include "mpeg.h"
|
||||
#include "audio.h"
|
||||
#include "power.h"
|
||||
|
|
@ -1528,6 +1527,14 @@ const struct settings_list settings[] = {
|
|||
tsc_is_changed, tsc_set_default),
|
||||
#endif
|
||||
OFFON_SETTING(0, prevent_skip, LANG_PREVENT_SKIPPING, false, "prevent track skip", NULL),
|
||||
|
||||
OFFON_SETTING(0, pitch_mode_semitone, LANG_SEMITONE, false,
|
||||
"Semitone pitch change", NULL),
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
OFFON_SETTING(0, pitch_mode_timestretch, LANG_TIMESTRETCH, false,
|
||||
"Timestretch mode", NULL),
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
const int nb_settings = sizeof(settings)/sizeof(*settings);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
#include "debug.h"
|
||||
#include "system.h"
|
||||
#include "tdspeed.h"
|
||||
#include "settings.h"
|
||||
|
|
@ -72,7 +71,7 @@ void tdspeed_init()
|
|||
}
|
||||
|
||||
|
||||
bool tdspeed_config(int samplerate, bool stereo, int factor)
|
||||
bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
|
||||
{
|
||||
struct tdspeed_state_s *st = &tdspeed_state;
|
||||
int src_frame_sz;
|
||||
|
|
@ -84,7 +83,7 @@ bool tdspeed_config(int samplerate, bool stereo, int factor)
|
|||
return false;
|
||||
|
||||
/* Check parameters */
|
||||
if (factor == 100)
|
||||
if (factor == PITCH_SPEED_100)
|
||||
return false;
|
||||
if (samplerate < MIN_RATE || samplerate > MAX_RATE)
|
||||
return false;
|
||||
|
|
@ -94,14 +93,14 @@ bool tdspeed_config(int samplerate, bool stereo, int factor)
|
|||
st->stereo = stereo;
|
||||
st->dst_step = samplerate / MINFREQ;
|
||||
|
||||
if (factor > 100)
|
||||
st->dst_step = st->dst_step * 100 / factor;
|
||||
if (factor > PITCH_SPEED_100)
|
||||
st->dst_step = st->dst_step * PITCH_SPEED_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->src_step = st->dst_step * factor / PITCH_SPEED_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;
|
||||
|
|
|
|||
|
|
@ -23,15 +23,28 @@
|
|||
#ifndef _TDSPEED_H
|
||||
#define _TDSPEED_H
|
||||
|
||||
#include "dsp.h"
|
||||
/* for the precision #defines: */
|
||||
#include "pitchscreen.h"
|
||||
|
||||
#define TDSPEED_OUTBUFSIZE 4096
|
||||
|
||||
/* some #define functions to get the pitch, stretch and speed values based on */
|
||||
/* two known values. Remember that params are alphabetical. */
|
||||
#define GET_SPEED(pitch, stretch) \
|
||||
((pitch * stretch + PITCH_SPEED_100 / 2L) / PITCH_SPEED_100)
|
||||
#define GET_PITCH(speed, stretch) \
|
||||
((speed * PITCH_SPEED_100 + stretch / 2L) / stretch)
|
||||
#define GET_STRETCH(pitch, speed) \
|
||||
((speed * PITCH_SPEED_100 + pitch / 2L) / pitch)
|
||||
|
||||
void tdspeed_init(void);
|
||||
bool tdspeed_config(int samplerate, bool stereo, int factor);
|
||||
bool tdspeed_config(int samplerate, bool stereo, int32_t factor);
|
||||
long tdspeed_est_output_size(void);
|
||||
long tdspeed_est_input_size(long size);
|
||||
int tdspeed_doit(int32_t *src[], int count);
|
||||
|
||||
#define STRETCH_MAX 250
|
||||
#define STRETCH_MIN 35
|
||||
#define STRETCH_MAX (250L * PITCH_SPEED_PRECISION) /* 250% */
|
||||
#define STRETCH_MIN (35L * PITCH_SPEED_PRECISION) /* 35% */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -479,6 +479,7 @@ Andre Lupa
|
|||
Hilton Shumway
|
||||
Matthew Bonnett
|
||||
Nick Tryon
|
||||
David Johnston
|
||||
|
||||
The libmad team
|
||||
The wavpack team
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ void sound_set(int setting, int value);
|
|||
int sound_val2phys(int setting, int value);
|
||||
|
||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
|
||||
void sound_set_pitch(int permille);
|
||||
int sound_get_pitch(void);
|
||||
void sound_set_pitch(int32_t pitch);
|
||||
int32_t sound_get_pitch(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include "sound.h"
|
||||
#include "logf.h"
|
||||
#include "system.h"
|
||||
/* for the pitch and speed precision #defines: */
|
||||
#include "pitchscreen.h"
|
||||
#ifndef SIMULATOR
|
||||
#include "i2c.h"
|
||||
#include "mas.h"
|
||||
|
|
@ -159,6 +161,7 @@ sound_set_type* sound_get_fn(int setting)
|
|||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
/* Copied from dsp.h, nasty nasty, but we don't want to include dsp.h */
|
||||
|
||||
enum {
|
||||
DSP_CALLBACK_SET_PRESCALE = 0,
|
||||
DSP_CALLBACK_SET_BASS,
|
||||
|
|
@ -698,18 +701,18 @@ int sound_val2phys(int setting, int value)
|
|||
crystal frequency than we actually have. It will adjust its internal
|
||||
parameters and the result is that the audio is played at another pitch.
|
||||
|
||||
The pitch value is in tenths of percent.
|
||||
The pitch value precision is based on PITCH_SPEED_PRECISION (in dsp.h)
|
||||
*/
|
||||
static int last_pitch = 1000;
|
||||
static int last_pitch = PITCH_SPEED_100;
|
||||
|
||||
void sound_set_pitch(int pitch)
|
||||
void sound_set_pitch(int32_t pitch)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
if (pitch != last_pitch)
|
||||
{
|
||||
/* Calculate the new (bogus) frequency */
|
||||
val = 18432 * 1000 / pitch;
|
||||
val = 18432 * PITCH_SPEED_100 / pitch;
|
||||
|
||||
mas_writemem(MAS_BANK_D0, MAS_D0_OFREQ_CONTROL, &val, 1);
|
||||
|
||||
|
|
@ -721,19 +724,19 @@ void sound_set_pitch(int pitch)
|
|||
}
|
||||
}
|
||||
|
||||
int sound_get_pitch(void)
|
||||
int32_t sound_get_pitch(void)
|
||||
{
|
||||
return last_pitch;
|
||||
}
|
||||
#else /* SIMULATOR */
|
||||
void sound_set_pitch(int pitch)
|
||||
void sound_set_pitch(int32_t pitch)
|
||||
{
|
||||
(void)pitch;
|
||||
}
|
||||
|
||||
int sound_get_pitch(void)
|
||||
int32_t sound_get_pitch(void)
|
||||
{
|
||||
return 1000;
|
||||
return PITCH_SPEED_100;
|
||||
}
|
||||
#endif /* SIMULATOR */
|
||||
#endif /* (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) */
|
||||
|
|
|
|||
|
|
@ -273,34 +273,52 @@ Delete the currently playing file.
|
|||
\nopt{player}{
|
||||
\subsubsection{\label{sec:pitchscreen}Pitch}
|
||||
|
||||
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.
|
||||
The \setting{Pitch Screen} allows you to change the rate of playback
|
||||
(i.e. the playback speed and at the same time the pitch) of your
|
||||
\dap. The rate value can be adjusted between 50\% and 200\%. 50\%
|
||||
means half the normal playback speed and a pitch that is an octave
|
||||
lower than the normal pitch. 200\% means double playback speed and a
|
||||
pitch that is an octave higher than the normal pitch.
|
||||
|
||||
The rate can be changed in two modes: procentual and semitone.
|
||||
Initially, procentual mode is active.
|
||||
|
||||
\opt{swcodec}{
|
||||
If you've enabled the \setting{Timestretch} option in
|
||||
\setting{Sound Settings} and have since rebooted, you can also use
|
||||
timestretch mode. This allows you to change the playback speed
|
||||
without affecting the pitch, and vice versa.
|
||||
|
||||
In timestretch mode there are separate displays for pitch and
|
||||
speed, and each can be altered independently. Due to the
|
||||
limitations of the algorithm, speed is limited to be between 35\%
|
||||
and 250\% of the current pitch value. Pitch must maintain the
|
||||
same ratio as well as remain between 50\% and 200\%.
|
||||
}
|
||||
|
||||
The value of the \opt{swcodec}{rate, pitch and speed}\nopt{swcodec}{rate}
|
||||
is not persisted, i.e. after the \dap\ is turned on it will
|
||||
always be set to 100\%.
|
||||
|
||||
\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)\\
|
||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or by 0.1
|
||||
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) \\
|
||||
& Temporarily change pitch by 2\% (beatmatch) \\
|
||||
%
|
||||
\ActionPsReset
|
||||
& Reset pitch to 100\% \\
|
||||
& Reset rate to 100\% \\
|
||||
%
|
||||
\ActionPsExit
|
||||
& Leave the Pitch Screen \\
|
||||
|
|
@ -312,23 +330,16 @@ Delete the currently playing file.
|
|||
}
|
||||
|
||||
\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
|
||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsToggleMode}
|
||||
& Toggle pitch changing mode \\
|
||||
& Toggle pitch changing mode (cycles through all available modes)\\
|
||||
%
|
||||
\ActionPsIncSmall{} / \ActionPsDecSmall
|
||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsIncSmall{} / \ActionRCPsDecSmall}
|
||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
|
||||
(in semitone mode)\\
|
||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or 0.1
|
||||
semitone (in semitone mode)\\
|
||||
%
|
||||
\ActionPsIncBig{} / \ActionPsDecBig
|
||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsIncBig{} / \ActionRCPsDecBig}
|
||||
|
|
@ -337,7 +348,7 @@ Delete the currently playing file.
|
|||
%
|
||||
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsNudgeLeft{} / \ActionPsNudgeRight}
|
||||
& Temporarily change pitch by 2.0\% (beatmatch), or modify speed (in timestretch mode) \\
|
||||
& Temporarily change pitch by 2\% (beatmatch), or modify speed (in timestretch mode) \\
|
||||
%
|
||||
\ActionPsReset
|
||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsReset}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue