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_depth;
|
||||||
int sample_bytes;
|
int sample_bytes;
|
||||||
int stereo_mode;
|
int stereo_mode;
|
||||||
int tdspeed_percent; /* Speed % */
|
int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */
|
||||||
bool tdspeed_active; /* Timestretch is in use */
|
bool tdspeed_active; /* Timestretch is in use */
|
||||||
int frac_bits;
|
int frac_bits;
|
||||||
#ifdef HAVE_SW_TONE_CONTROLS
|
#ifdef HAVE_SW_TONE_CONTROLS
|
||||||
|
|
@ -205,7 +205,7 @@ static int treble; /* A/V */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Settings applicable to audio codec only */
|
/* Settings applicable to audio codec only */
|
||||||
static int pitch_ratio = 1000;
|
static int32_t pitch_ratio = PITCH_SPEED_100;
|
||||||
static int channels_mode;
|
static int channels_mode;
|
||||||
long dsp_sw_gain;
|
long dsp_sw_gain;
|
||||||
long dsp_sw_cross;
|
long dsp_sw_cross;
|
||||||
|
|
@ -254,14 +254,14 @@ static inline int32_t clip_sample_16(int32_t sample)
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sound_get_pitch(void)
|
int32_t sound_get_pitch(void)
|
||||||
{
|
{
|
||||||
return pitch_ratio;
|
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,
|
dsp_configure(&AUDIO_DSP, DSP_SWITCH_FREQUENCY,
|
||||||
AUDIO_DSP.codec_frequency);
|
AUDIO_DSP.codec_frequency);
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +277,7 @@ static void tdspeed_setup(struct dsp_config *dspc)
|
||||||
if(!dsp_timestretch_available())
|
if(!dsp_timestretch_available())
|
||||||
return; /* Timestretch not enabled or buffer not allocated */
|
return; /* Timestretch not enabled or buffer not allocated */
|
||||||
if (dspc->tdspeed_percent == 0)
|
if (dspc->tdspeed_percent == 0)
|
||||||
dspc->tdspeed_percent = 100;
|
dspc->tdspeed_percent = PITCH_SPEED_100;
|
||||||
if (!tdspeed_config(
|
if (!tdspeed_config(
|
||||||
dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency,
|
dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency,
|
||||||
dspc->stereo_mode != STEREO_MONO,
|
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;
|
AUDIO_DSP.tdspeed_percent = percent;
|
||||||
tdspeed_setup(&AUDIO_DSP);
|
tdspeed_setup(&AUDIO_DSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dsp_get_timestretch()
|
int32_t dsp_get_timestretch()
|
||||||
{
|
{
|
||||||
return AUDIO_DSP.tdspeed_percent;
|
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.
|
not need this feature.
|
||||||
*/
|
*/
|
||||||
if (dsp == &AUDIO_DSP)
|
if (dsp == &AUDIO_DSP)
|
||||||
dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000;
|
dsp->frequency = pitch_ratio * dsp->codec_frequency / PITCH_SPEED_100;
|
||||||
else
|
else
|
||||||
dsp->frequency = dsp->codec_frequency;
|
dsp->frequency = dsp->codec_frequency;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,10 +83,10 @@ void dsp_set_eq_coefs(int band);
|
||||||
void dsp_dither_enable(bool enable);
|
void dsp_dither_enable(bool enable);
|
||||||
void dsp_timestretch_enable(bool enable);
|
void dsp_timestretch_enable(bool enable);
|
||||||
bool dsp_timestretch_available(void);
|
bool dsp_timestretch_available(void);
|
||||||
void sound_set_pitch(int r);
|
void sound_set_pitch(int32_t r);
|
||||||
int sound_get_pitch(void);
|
int32_t sound_get_pitch(void);
|
||||||
void dsp_set_timestretch(int percent);
|
void dsp_set_timestretch(int32_t percent);
|
||||||
int dsp_get_timestretch(void);
|
int32_t dsp_get_timestretch(void);
|
||||||
int dsp_callback(int msg, intptr_t param);
|
int dsp_callback(int msg, intptr_t param);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "sprintf.h"
|
#include "sprintf.h"
|
||||||
#include "action.h"
|
#include "action.h"
|
||||||
|
|
@ -36,24 +37,27 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "pitchscreen.h"
|
#include "pitchscreen.h"
|
||||||
|
#include "settings.h"
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
#include "tdspeed.h"
|
#include "tdspeed.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ABS(x) ((x) > 0 ? (x) : -(x))
|
||||||
|
|
||||||
#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
|
#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
|
||||||
/* on both sides when drawing */
|
/* on both sides when drawing */
|
||||||
|
|
||||||
#define PITCH_MAX 2000
|
#define PITCH_MAX (200 * PITCH_SPEED_PRECISION)
|
||||||
#define PITCH_MIN 500
|
#define PITCH_MIN (50 * PITCH_SPEED_PRECISION)
|
||||||
#define PITCH_SMALL_DELTA 1
|
#define PITCH_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
|
||||||
#define PITCH_BIG_DELTA 10
|
#define PITCH_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
|
||||||
#define PITCH_NUDGE_DELTA 20
|
#define PITCH_NUDGE_DELTA (2 * PITCH_SPEED_PRECISION) /* 2% */
|
||||||
|
|
||||||
static bool pitch_mode_semitone = false;
|
#define SPEED_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* .1% */
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#define SPEED_BIG_DELTA (PITCH_SPEED_PRECISION) /* 1% */
|
||||||
static bool pitch_mode_timestretch = false;
|
|
||||||
#endif
|
#define SEMITONE_SMALL_DELTA (PITCH_SPEED_PRECISION / 10) /* 10 cents */
|
||||||
|
#define SEMITONE_BIG_DELTA PITCH_SPEED_PRECISION /* 1 semitone */
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
@ -63,25 +67,111 @@ enum
|
||||||
PITCH_ITEM_COUNT,
|
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,
|
static void pitchscreen_fix_viewports(struct viewport *parent,
|
||||||
struct viewport pitch_viewports[PITCH_ITEM_COUNT])
|
struct viewport pitch_viewports[PITCH_ITEM_COUNT])
|
||||||
{
|
{
|
||||||
int i, height;
|
int i, font_height;
|
||||||
height = font_get(parent->font)->height;
|
font_height = font_get(parent->font)->height;
|
||||||
for (i = 0; i < PITCH_ITEM_COUNT; i++)
|
for (i = 0; i < PITCH_ITEM_COUNT; i++)
|
||||||
{
|
{
|
||||||
pitch_viewports[i] = *parent;
|
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_TOP].y += ICON_BORDER;
|
||||||
|
|
||||||
pitch_viewports[PITCH_MID].x += ICON_BORDER;
|
pitch_viewports[PITCH_MID].x += ICON_BORDER;
|
||||||
pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
|
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].y += parent->height / 2 -
|
||||||
pitch_viewports[PITCH_MID].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
|
/* 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,
|
static void pitchscreen_draw(struct screen *display, int max_lines,
|
||||||
struct viewport pitch_viewports[PITCH_ITEM_COUNT],
|
struct viewport pitch_viewports[PITCH_ITEM_COUNT],
|
||||||
int pitch
|
int32_t pitch, int32_t semitone
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
,int speed
|
,int32_t speed
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -123,7 +213,7 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
||||||
{
|
{
|
||||||
/* UP: Pitch Up */
|
/* UP: Pitch Up */
|
||||||
display->set_viewport(&pitch_viewports[PITCH_TOP]);
|
display->set_viewport(&pitch_viewports[PITCH_TOP]);
|
||||||
if (pitch_mode_semitone)
|
if (global_settings.pitch_mode_semitone)
|
||||||
ptr = str(LANG_PITCH_UP_SEMITONE);
|
ptr = str(LANG_PITCH_UP_SEMITONE);
|
||||||
else
|
else
|
||||||
ptr = str(LANG_PITCH_UP);
|
ptr = str(LANG_PITCH_UP);
|
||||||
|
|
@ -136,7 +226,7 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
||||||
|
|
||||||
/* DOWN: Pitch Down */
|
/* DOWN: Pitch Down */
|
||||||
display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
|
display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
|
||||||
if (pitch_mode_semitone)
|
if (global_settings.pitch_mode_semitone)
|
||||||
ptr = str(LANG_PITCH_DOWN_SEMITONE);
|
ptr = str(LANG_PITCH_DOWN_SEMITONE);
|
||||||
else
|
else
|
||||||
ptr = str(LANG_PITCH_DOWN);
|
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 ((show_lang_pitch = (max_lines >= 3)))
|
||||||
{
|
{
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#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
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* Pitch:XXX.X% */
|
/* Pitch:XXX.X% */
|
||||||
snprintf(buf, sizeof(buf), "%s:%d.%d%%", str(LANG_PITCH),
|
if(global_settings.pitch_mode_semitone)
|
||||||
pitch / 10, pitch % 10);
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_PITCH),
|
||||||
|
pitch / PITCH_SPEED_PRECISION,
|
||||||
|
(pitch % PITCH_SPEED_PRECISION) /
|
||||||
|
(PITCH_SPEED_PRECISION / 10));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
/* Rate */
|
||||||
|
snprintf(buf, sizeof(buf), "%s:", str(LANG_PLAYBACK_RATE));
|
||||||
|
}
|
||||||
display->getstringsize(buf, &w, &h);
|
display->getstringsize(buf, &w, &h);
|
||||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
||||||
0, buf);
|
(pitch_viewports[PITCH_MID].height / 2) - h, buf);
|
||||||
if (w > width_used)
|
if (w > width_used)
|
||||||
width_used = w;
|
width_used = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Middle section lower line */
|
/* Middle section lower line */
|
||||||
|
/* "Speed:XXX%" */
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
if (!pitch_mode_timestretch)
|
if(global_settings.pitch_mode_timestretch)
|
||||||
{
|
{
|
||||||
#endif
|
snprintf(buf, sizeof(buf), "%s: %ld.%ld%%", str(LANG_SPEED),
|
||||||
/* "XXX.X%" */
|
speed / PITCH_SPEED_PRECISION,
|
||||||
snprintf(buf, sizeof(buf), "%d.%d%%",
|
(speed % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
|
||||||
pitch / 10, pitch % 10);
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
/* "Speed:XXX%" */
|
|
||||||
snprintf(buf, sizeof(buf), "%s:%d%%", str(LANG_SPEED),
|
|
||||||
speed / 1000);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
snprintf(buf, sizeof(buf), "%ld.%ld%%",
|
||||||
|
pitch / PITCH_SPEED_PRECISION,
|
||||||
|
(pitch % PITCH_SPEED_PRECISION) / (PITCH_SPEED_PRECISION / 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
display->getstringsize(buf, &w, &h);
|
display->getstringsize(buf, &w, &h);
|
||||||
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
|
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)
|
if (w > width_used)
|
||||||
width_used = w;
|
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 */
|
/* Middle section left/right labels */
|
||||||
const char *leftlabel = "-2%";
|
const char *leftlabel = "-2%";
|
||||||
const char *rightlabel = "+2%";
|
const char *rightlabel = "+2%";
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
if (pitch_mode_timestretch)
|
if (global_settings.pitch_mode_timestretch)
|
||||||
{
|
{
|
||||||
leftlabel = "<<";
|
leftlabel = "<<";
|
||||||
rightlabel = ">>";
|
rightlabel = ">>";
|
||||||
|
|
@ -220,37 +350,67 @@ static void pitchscreen_draw(struct screen *display, int max_lines,
|
||||||
|
|
||||||
if (width_used <= pitch_viewports[PITCH_MID].width)
|
if (width_used <= pitch_viewports[PITCH_MID].width)
|
||||||
{
|
{
|
||||||
display->putsxy(0, h / 2, leftlabel);
|
display->putsxy(0, (pitch_viewports[PITCH_MID].height / 2) - (h / 2),
|
||||||
display->putsxy(pitch_viewports[PITCH_MID].width - w, h /2, rightlabel);
|
leftlabel);
|
||||||
|
display->putsxy((pitch_viewports[PITCH_MID].width - w),
|
||||||
|
(pitch_viewports[PITCH_MID].height / 2) - (h / 2),
|
||||||
|
rightlabel);
|
||||||
}
|
}
|
||||||
display->update_viewport();
|
display->update_viewport();
|
||||||
display->set_viewport(NULL);
|
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_delta < 0)
|
||||||
{
|
{
|
||||||
if (pitch + pitch_delta >= PITCH_MIN)
|
/* for large jumps, snap up to whole numbers */
|
||||||
new_pitch = pitch + pitch_delta;
|
if(allow_cutoff && pitch_delta <= -PITCH_SPEED_PRECISION &&
|
||||||
else
|
(pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
|
||||||
|
{
|
||||||
|
pitch_delta += PITCH_SPEED_PRECISION - ((pitch + pitch_delta) % PITCH_SPEED_PRECISION);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_pitch = pitch + pitch_delta;
|
||||||
|
|
||||||
|
if (new_pitch < PITCH_MIN)
|
||||||
{
|
{
|
||||||
if (!allow_cutoff)
|
if (!allow_cutoff)
|
||||||
|
{
|
||||||
return pitch;
|
return pitch;
|
||||||
|
}
|
||||||
new_pitch = PITCH_MIN;
|
new_pitch = PITCH_MIN;
|
||||||
|
at_limit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pitch_delta > 0)
|
else if (pitch_delta > 0)
|
||||||
{
|
{
|
||||||
if (pitch + pitch_delta <= PITCH_MAX)
|
/* for large jumps, snap down to whole numbers */
|
||||||
new_pitch = pitch + pitch_delta;
|
if(allow_cutoff && pitch_delta >= PITCH_SPEED_PRECISION &&
|
||||||
else
|
(pitch + pitch_delta) % PITCH_SPEED_PRECISION != 0)
|
||||||
|
{
|
||||||
|
pitch_delta -= (pitch + pitch_delta) % PITCH_SPEED_PRECISION;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_pitch = pitch + pitch_delta;
|
||||||
|
|
||||||
|
if (new_pitch > PITCH_MAX)
|
||||||
{
|
{
|
||||||
if (!allow_cutoff)
|
if (!allow_cutoff)
|
||||||
return pitch;
|
return pitch;
|
||||||
new_pitch = PITCH_MAX;
|
new_pitch = PITCH_MAX;
|
||||||
|
at_limit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -258,47 +418,164 @@ static int pitch_increase(int pitch, int pitch_delta, bool allow_cutoff)
|
||||||
/* pitch_delta == 0 -> no real change */
|
/* pitch_delta == 0 -> no real change */
|
||||||
return pitch;
|
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);
|
sound_set_pitch(new_pitch);
|
||||||
|
|
||||||
return new_pitch;
|
return new_pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Factor for changing the pitch one half tone up.
|
static int32_t get_semitone_from_pitch(int32_t pitch)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
uint32_t tmp;
|
int semitone = 0;
|
||||||
uint32_t round_fct; /* How much to scale down at the end */
|
int32_t fractional_index = 0;
|
||||||
tmp = pitch;
|
|
||||||
if (up)
|
while(semitone < NUM_SEMITONES - 1 &&
|
||||||
|
pitch >= semitone_table[semitone + 1])
|
||||||
{
|
{
|
||||||
tmp = tmp * PITCH_SEMITONE_FACTOR;
|
semitone++;
|
||||||
round_fct = PITCH_K_FCT;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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
|
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;
|
new_semitone = SEMITONE_START * PITCH_SPEED_PRECISION;
|
||||||
round_fct = PITCH_N_FCT;
|
at_limit = true;
|
||||||
}
|
}
|
||||||
/* Scaling down with rounding */
|
else if(new_semitone > (SEMITONE_END * PITCH_SPEED_PRECISION))
|
||||||
tmp = (tmp + round_fct / 2) / round_fct;
|
{
|
||||||
return pitch_increase(pitch, tmp - pitch, false);
|
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 gui_syncpitchscreen_run(void)
|
||||||
{
|
{
|
||||||
int button, i;
|
int button, i;
|
||||||
int pitch = sound_get_pitch();
|
int32_t pitch = sound_get_pitch();
|
||||||
#if CONFIG_CODEC == SWCODEC
|
int32_t semitone;
|
||||||
int stretch = dsp_get_timestretch();
|
|
||||||
int speed = stretch * pitch; /* speed to maintain */
|
int32_t new_pitch;
|
||||||
#endif
|
int32_t pitch_delta;
|
||||||
int new_pitch;
|
|
||||||
int pitch_delta;
|
|
||||||
bool nudged = false;
|
bool nudged = false;
|
||||||
bool exit = false;
|
bool exit = false;
|
||||||
/* should maybe be passed per parameter later, not needed for now */
|
/* 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];
|
struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
|
||||||
int max_lines[NB_SCREENS];
|
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 */
|
/* initialize pitchscreen vps */
|
||||||
FOR_NB_SCREENS(i)
|
FOR_NB_SCREENS(i)
|
||||||
{
|
{
|
||||||
|
|
@ -343,49 +643,80 @@ int gui_syncpitchscreen_run(void)
|
||||||
{
|
{
|
||||||
FOR_NB_SCREENS(i)
|
FOR_NB_SCREENS(i)
|
||||||
pitchscreen_draw(&screens[i], max_lines[i],
|
pitchscreen_draw(&screens[i], max_lines[i],
|
||||||
pitch_viewports[i], pitch
|
pitch_viewports[i], pitch, semitone
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
, speed
|
, speed
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
pitch_delta = 0;
|
pitch_delta = 0;
|
||||||
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
new_speed = 0;
|
||||||
|
#endif
|
||||||
button = get_action(CONTEXT_PITCHSCREEN, HZ);
|
button = get_action(CONTEXT_PITCHSCREEN, HZ);
|
||||||
switch (button)
|
switch (button)
|
||||||
{
|
{
|
||||||
case ACTION_PS_INC_SMALL:
|
case ACTION_PS_INC_SMALL:
|
||||||
pitch_delta = PITCH_SMALL_DELTA;
|
if(global_settings.pitch_mode_semitone)
|
||||||
|
pitch_delta = SEMITONE_SMALL_DELTA;
|
||||||
|
else
|
||||||
|
pitch_delta = PITCH_SMALL_DELTA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_INC_BIG:
|
case ACTION_PS_INC_BIG:
|
||||||
pitch_delta = PITCH_BIG_DELTA;
|
if(global_settings.pitch_mode_semitone)
|
||||||
|
pitch_delta = SEMITONE_BIG_DELTA;
|
||||||
|
else
|
||||||
|
pitch_delta = PITCH_BIG_DELTA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_DEC_SMALL:
|
case ACTION_PS_DEC_SMALL:
|
||||||
pitch_delta = -PITCH_SMALL_DELTA;
|
if(global_settings.pitch_mode_semitone)
|
||||||
|
pitch_delta = -SEMITONE_SMALL_DELTA;
|
||||||
|
else
|
||||||
|
pitch_delta = -PITCH_SMALL_DELTA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_DEC_BIG:
|
case ACTION_PS_DEC_BIG:
|
||||||
pitch_delta = -PITCH_BIG_DELTA;
|
if(global_settings.pitch_mode_semitone)
|
||||||
|
pitch_delta = -SEMITONE_BIG_DELTA;
|
||||||
|
else
|
||||||
|
pitch_delta = -PITCH_BIG_DELTA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_NUDGE_RIGHT:
|
case ACTION_PS_NUDGE_RIGHT:
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
if (!pitch_mode_timestretch)
|
if (!global_settings.pitch_mode_timestretch)
|
||||||
{
|
{
|
||||||
#endif
|
#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);
|
nudged = (new_pitch != pitch);
|
||||||
pitch = new_pitch;
|
pitch = new_pitch;
|
||||||
|
semitone = get_semitone_from_pitch(pitch);
|
||||||
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
speed = pitch;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_speed = speed + SPEED_SMALL_DELTA;
|
||||||
|
at_limit = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ACTION_PS_FASTER:
|
case ACTION_PS_FASTER:
|
||||||
if (pitch_mode_timestretch && stretch < STRETCH_MAX)
|
if (global_settings.pitch_mode_timestretch)
|
||||||
{
|
{
|
||||||
stretch++;
|
new_speed = speed + SPEED_BIG_DELTA;
|
||||||
dsp_set_timestretch(stretch);
|
/* snap to whole numbers */
|
||||||
speed = stretch * pitch;
|
if(new_speed % PITCH_SPEED_PRECISION != 0)
|
||||||
|
new_speed -= new_speed % PITCH_SPEED_PRECISION;
|
||||||
|
at_limit = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -393,29 +724,53 @@ int gui_syncpitchscreen_run(void)
|
||||||
case ACTION_PS_NUDGE_RIGHTOFF:
|
case ACTION_PS_NUDGE_RIGHTOFF:
|
||||||
if (nudged)
|
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;
|
nudged = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_NUDGE_LEFT:
|
case ACTION_PS_NUDGE_LEFT:
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
if (!pitch_mode_timestretch)
|
if (!global_settings.pitch_mode_timestretch)
|
||||||
{
|
{
|
||||||
#endif
|
#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);
|
nudged = (new_pitch != pitch);
|
||||||
pitch = new_pitch;
|
pitch = new_pitch;
|
||||||
|
semitone = get_semitone_from_pitch(pitch);
|
||||||
|
#if CONFIG_CODEC == SWCODEC
|
||||||
|
speed = pitch;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_speed = speed - SPEED_SMALL_DELTA;
|
||||||
|
at_limit = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ACTION_PS_SLOWER:
|
case ACTION_PS_SLOWER:
|
||||||
if (pitch_mode_timestretch && stretch > STRETCH_MIN)
|
if (global_settings.pitch_mode_timestretch)
|
||||||
{
|
{
|
||||||
stretch--;
|
new_speed = speed - SPEED_BIG_DELTA;
|
||||||
dsp_set_timestretch(stretch);
|
/* snap to whole numbers */
|
||||||
speed = stretch * pitch;
|
if(new_speed % PITCH_SPEED_PRECISION != 0)
|
||||||
|
new_speed += PITCH_SPEED_PRECISION - speed % PITCH_SPEED_PRECISION;
|
||||||
|
at_limit = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -423,27 +778,49 @@ int gui_syncpitchscreen_run(void)
|
||||||
case ACTION_PS_NUDGE_LEFTOFF:
|
case ACTION_PS_NUDGE_LEFTOFF:
|
||||||
if (nudged)
|
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;
|
nudged = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_RESET:
|
case ACTION_PS_RESET:
|
||||||
pitch = 1000;
|
pitch = PITCH_SPEED_100;
|
||||||
sound_set_pitch(pitch);
|
sound_set_pitch(pitch);
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
stretch = 100;
|
speed = PITCH_SPEED_100;
|
||||||
dsp_set_timestretch(stretch);
|
if (dsp_timestretch_available())
|
||||||
speed = stretch * pitch;
|
{
|
||||||
|
dsp_set_timestretch(PITCH_SPEED_100);
|
||||||
|
at_limit = false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
semitone = get_semitone_from_pitch(pitch);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_TOGGLE_MODE:
|
case ACTION_PS_TOGGLE_MODE:
|
||||||
|
global_settings.pitch_mode_semitone = !global_settings.pitch_mode_semitone;
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#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
|
#endif
|
||||||
pitch_mode_semitone = !pitch_mode_semitone;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_PS_EXIT:
|
case ACTION_PS_EXIT:
|
||||||
|
|
@ -457,27 +834,73 @@ int gui_syncpitchscreen_run(void)
|
||||||
}
|
}
|
||||||
if (pitch_delta)
|
if (pitch_delta)
|
||||||
{
|
{
|
||||||
if (pitch_mode_semitone)
|
if (global_settings.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_timestretch)
|
|
||||||
{
|
{
|
||||||
/* Set stretch to maintain speed */
|
semitone = pitch_increase_semitone(pitch, semitone, pitch_delta
|
||||||
/* i.e. increase pitch, reduce stretch */
|
#if CONFIG_CODEC == SWCODEC
|
||||||
int new_stretch = speed / pitch;
|
, speed
|
||||||
if (new_stretch >= STRETCH_MIN && new_stretch <= STRETCH_MAX)
|
#endif
|
||||||
{
|
);
|
||||||
stretch = new_stretch;
|
pitch = get_pitch_from_semitone(semitone);
|
||||||
dsp_set_timestretch(stretch);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
speed = stretch * pitch;
|
{
|
||||||
#endif
|
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
|
#if CONFIG_CODEC == SWCODEC
|
||||||
pcmbuf_set_low_latency(false);
|
pcmbuf_set_low_latency(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@
|
||||||
#ifndef _PITCHSCREEN_H_
|
#ifndef _PITCHSCREEN_H_
|
||||||
#define _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);
|
int gui_syncpitchscreen_run(void);
|
||||||
|
|
||||||
#endif /* _PITCHSCREEN_H_ */
|
#endif /* _PITCHSCREEN_H_ */
|
||||||
|
|
|
||||||
|
|
@ -12604,3 +12604,45 @@
|
||||||
remote: "Remote Statusbar"
|
remote: "Remote Statusbar"
|
||||||
</voice>
|
</voice>
|
||||||
</phrase>
|
</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 */
|
#define PLUGIN_MAGIC 0x526F634B /* RocK */
|
||||||
|
|
||||||
/* increase this every time the api struct changes */
|
/* 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
|
/* update this to latest version if a change to the api struct breaks
|
||||||
backwards compatibility (and please take the opportunity to sort in any
|
backwards compatibility (and please take the opportunity to sort in any
|
||||||
new function which are "waiting" at the end of the function table) */
|
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 */
|
/* plugin return codes */
|
||||||
enum plugin_status {
|
enum plugin_status {
|
||||||
|
|
@ -618,7 +618,7 @@ struct plugin_api {
|
||||||
#endif
|
#endif
|
||||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) || \
|
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) || \
|
||||||
(CONFIG_CODEC == SWCODEC)
|
(CONFIG_CODEC == SWCODEC)
|
||||||
void (*sound_set_pitch)(int pitch);
|
void (*sound_set_pitch)(int32_t pitch);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* MAS communication */
|
/* MAS communication */
|
||||||
|
|
|
||||||
|
|
@ -739,6 +739,11 @@ struct user_settings
|
||||||
struct touchscreen_parameter ts_calibration_data;
|
struct touchscreen_parameter ts_calibration_data;
|
||||||
#endif
|
#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
|
/* If values are just added to the end, no need to bump plugin API
|
||||||
version. */
|
version. */
|
||||||
/* new stuff to be added at the end */
|
/* new stuff to be added at the end */
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@
|
||||||
#include "settings_list.h"
|
#include "settings_list.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
#include "debug.h"
|
|
||||||
#include "mpeg.h"
|
#include "mpeg.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
@ -1528,6 +1527,14 @@ const struct settings_list settings[] = {
|
||||||
tsc_is_changed, tsc_set_default),
|
tsc_is_changed, tsc_set_default),
|
||||||
#endif
|
#endif
|
||||||
OFFON_SETTING(0, prevent_skip, LANG_PREVENT_SKIPPING, false, "prevent track skip", NULL),
|
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);
|
const int nb_settings = sizeof(settings)/sizeof(*settings);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "debug.h"
|
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "tdspeed.h"
|
#include "tdspeed.h"
|
||||||
#include "settings.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;
|
struct tdspeed_state_s *st = &tdspeed_state;
|
||||||
int src_frame_sz;
|
int src_frame_sz;
|
||||||
|
|
@ -84,7 +83,7 @@ bool tdspeed_config(int samplerate, bool stereo, int factor)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Check parameters */
|
/* Check parameters */
|
||||||
if (factor == 100)
|
if (factor == PITCH_SPEED_100)
|
||||||
return false;
|
return false;
|
||||||
if (samplerate < MIN_RATE || samplerate > MAX_RATE)
|
if (samplerate < MIN_RATE || samplerate > MAX_RATE)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -94,14 +93,14 @@ bool tdspeed_config(int samplerate, bool stereo, int factor)
|
||||||
st->stereo = stereo;
|
st->stereo = stereo;
|
||||||
st->dst_step = samplerate / MINFREQ;
|
st->dst_step = samplerate / MINFREQ;
|
||||||
|
|
||||||
if (factor > 100)
|
if (factor > PITCH_SPEED_100)
|
||||||
st->dst_step = st->dst_step * 100 / factor;
|
st->dst_step = st->dst_step * PITCH_SPEED_100 / factor;
|
||||||
st->dst_order = 1;
|
st->dst_order = 1;
|
||||||
|
|
||||||
while (st->dst_step >>= 1)
|
while (st->dst_step >>= 1)
|
||||||
st->dst_order++;
|
st->dst_order++;
|
||||||
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;
|
st->shift_max = (st->dst_step > st->src_step) ? st->dst_step : st->src_step;
|
||||||
|
|
||||||
src_frame_sz = st->shift_max + st->dst_step;
|
src_frame_sz = st->shift_max + st->dst_step;
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,28 @@
|
||||||
#ifndef _TDSPEED_H
|
#ifndef _TDSPEED_H
|
||||||
#define _TDSPEED_H
|
#define _TDSPEED_H
|
||||||
|
|
||||||
|
#include "dsp.h"
|
||||||
|
/* for the precision #defines: */
|
||||||
|
#include "pitchscreen.h"
|
||||||
|
|
||||||
#define TDSPEED_OUTBUFSIZE 4096
|
#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);
|
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_output_size(void);
|
||||||
long tdspeed_est_input_size(long size);
|
long tdspeed_est_input_size(long size);
|
||||||
int tdspeed_doit(int32_t *src[], int count);
|
int tdspeed_doit(int32_t *src[], int count);
|
||||||
|
|
||||||
#define STRETCH_MAX 250
|
#define STRETCH_MAX (250L * PITCH_SPEED_PRECISION) /* 250% */
|
||||||
#define STRETCH_MIN 35
|
#define STRETCH_MIN (35L * PITCH_SPEED_PRECISION) /* 35% */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,7 @@ Andre Lupa
|
||||||
Hilton Shumway
|
Hilton Shumway
|
||||||
Matthew Bonnett
|
Matthew Bonnett
|
||||||
Nick Tryon
|
Nick Tryon
|
||||||
|
David Johnston
|
||||||
|
|
||||||
The libmad team
|
The libmad team
|
||||||
The wavpack team
|
The wavpack team
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ void sound_set(int setting, int value);
|
||||||
int sound_val2phys(int setting, int value);
|
int sound_val2phys(int setting, int value);
|
||||||
|
|
||||||
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
|
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
|
||||||
void sound_set_pitch(int permille);
|
void sound_set_pitch(int32_t pitch);
|
||||||
int sound_get_pitch(void);
|
int32_t sound_get_pitch(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "logf.h"
|
#include "logf.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
/* for the pitch and speed precision #defines: */
|
||||||
|
#include "pitchscreen.h"
|
||||||
#ifndef SIMULATOR
|
#ifndef SIMULATOR
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "mas.h"
|
#include "mas.h"
|
||||||
|
|
@ -159,6 +161,7 @@ sound_set_type* sound_get_fn(int setting)
|
||||||
|
|
||||||
#if CONFIG_CODEC == SWCODEC
|
#if CONFIG_CODEC == SWCODEC
|
||||||
/* Copied from dsp.h, nasty nasty, but we don't want to include dsp.h */
|
/* Copied from dsp.h, nasty nasty, but we don't want to include dsp.h */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DSP_CALLBACK_SET_PRESCALE = 0,
|
DSP_CALLBACK_SET_PRESCALE = 0,
|
||||||
DSP_CALLBACK_SET_BASS,
|
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
|
crystal frequency than we actually have. It will adjust its internal
|
||||||
parameters and the result is that the audio is played at another pitch.
|
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;
|
unsigned long val;
|
||||||
|
|
||||||
if (pitch != last_pitch)
|
if (pitch != last_pitch)
|
||||||
{
|
{
|
||||||
/* Calculate the new (bogus) frequency */
|
/* 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);
|
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;
|
return last_pitch;
|
||||||
}
|
}
|
||||||
#else /* SIMULATOR */
|
#else /* SIMULATOR */
|
||||||
void sound_set_pitch(int pitch)
|
void sound_set_pitch(int32_t pitch)
|
||||||
{
|
{
|
||||||
(void)pitch;
|
(void)pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sound_get_pitch(void)
|
int32_t sound_get_pitch(void)
|
||||||
{
|
{
|
||||||
return 1000;
|
return PITCH_SPEED_100;
|
||||||
}
|
}
|
||||||
#endif /* SIMULATOR */
|
#endif /* SIMULATOR */
|
||||||
#endif /* (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) */
|
#endif /* (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) */
|
||||||
|
|
|
||||||
|
|
@ -273,34 +273,52 @@ Delete the currently playing file.
|
||||||
\nopt{player}{
|
\nopt{player}{
|
||||||
\subsubsection{\label{sec:pitchscreen}Pitch}
|
\subsubsection{\label{sec:pitchscreen}Pitch}
|
||||||
|
|
||||||
The \setting{Pitch Screen} allows you to change the pitch and the playback
|
The \setting{Pitch Screen} allows you to change the rate of playback
|
||||||
speed of your \dap. The pitch value can be adjusted between 50\% and 200\%.
|
(i.e. the playback speed and at the same time the pitch) of your
|
||||||
50\% means half the normal playback speed and the pitch that is an octave lower
|
\dap. The rate value can be adjusted between 50\% and 200\%. 50\%
|
||||||
than the normal pitch. 200\% means double playback speed and the pitch that
|
means half the normal playback speed and a pitch that is an octave
|
||||||
is an octave higher than the normal pitch.
|
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}{
|
\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{table}
|
||||||
\begin{btnmap}{}{}
|
\begin{btnmap}{}{}
|
||||||
\ActionPsToggleMode
|
\ActionPsToggleMode
|
||||||
& Toggle pitch changing mode \\
|
& Toggle pitch changing mode \\
|
||||||
%
|
%
|
||||||
\ActionPsIncSmall{} / \ActionPsDecSmall
|
\ActionPsIncSmall{} / \ActionPsDecSmall
|
||||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
|
& Increase / Decrease pitch by 0.1\% (in procentual mode) or by 0.1
|
||||||
(in semitone mode)\\
|
semitone (in semitone mode)\\
|
||||||
%
|
%
|
||||||
\ActionPsIncBig{} / \ActionPsDecBig
|
\ActionPsIncBig{} / \ActionPsDecBig
|
||||||
& Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
|
& Increase / Decrease pitch by 1\% (in procentual mode) or a semitone
|
||||||
(in semitone mode)\\
|
(in semitone mode)\\
|
||||||
%
|
%
|
||||||
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
||||||
& Temporarily change pitch by 2.0\% (beatmatch) \\
|
& Temporarily change pitch by 2\% (beatmatch) \\
|
||||||
%
|
%
|
||||||
\ActionPsReset
|
\ActionPsReset
|
||||||
& Reset pitch to 100\% \\
|
& Reset rate to 100\% \\
|
||||||
%
|
%
|
||||||
\ActionPsExit
|
\ActionPsExit
|
||||||
& Leave the Pitch Screen \\
|
& Leave the Pitch Screen \\
|
||||||
|
|
@ -312,23 +330,16 @@ Delete the currently playing file.
|
||||||
}
|
}
|
||||||
|
|
||||||
\opt{swcodec}{
|
\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{table}
|
||||||
\begin{btnmap}{}{}
|
\begin{btnmap}{}{}
|
||||||
\ActionPsToggleMode
|
\ActionPsToggleMode
|
||||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsToggleMode}
|
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsToggleMode}
|
||||||
& Toggle pitch changing mode \\
|
& Toggle pitch changing mode (cycles through all available modes)\\
|
||||||
%
|
%
|
||||||
\ActionPsIncSmall{} / \ActionPsDecSmall
|
\ActionPsIncSmall{} / \ActionPsDecSmall
|
||||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsIncSmall{} / \ActionRCPsDecSmall}
|
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsIncSmall{} / \ActionRCPsDecSmall}
|
||||||
& Increase / Decrease pitch by 0.1\% (in procentual mode) or a semitone
|
& Increase / Decrease pitch by 0.1\% (in procentual mode) or 0.1
|
||||||
(in semitone mode)\\
|
semitone (in semitone mode)\\
|
||||||
%
|
%
|
||||||
\ActionPsIncBig{} / \ActionPsDecBig
|
\ActionPsIncBig{} / \ActionPsDecBig
|
||||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsIncBig{} / \ActionRCPsDecBig}
|
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsIncBig{} / \ActionRCPsDecBig}
|
||||||
|
|
@ -337,7 +348,7 @@ Delete the currently playing file.
|
||||||
%
|
%
|
||||||
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
\ActionPsNudgeLeft{} / \ActionPsNudgeRight
|
||||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsNudgeLeft{} / \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
|
\ActionPsReset
|
||||||
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsReset}
|
\opt{HAVEREMOTEKEYMAP}{& \ActionRCPsReset}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue