1
0
Fork 0
forked from len0rd/rockbox

pitch_detector: cleanup

- cosmetics: remove trailing white space
- mark all functions and variables as static
- merge struct definition and declaration when possible
- rename tuner_settings -> settings (because it's shorter)
- remove unused enums
- don't give pointer to settings struct as argument since there is only
  one struct, same for the settings filename
- fix error cases in settings load:
    reset settings when loading failed
    close file when it hasn't the right size
- inline small load/save functions only used once
- remove unused print_char_xy
- inline print_str and print_int_xy, and use lcd_putsf (added to the
  plugin API)

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27918 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Rafaël Carré 2010-08-28 17:52:31 +00:00
parent 5628096e51
commit e09ebc4213
3 changed files with 171 additions and 229 deletions

View file

@ -721,6 +721,8 @@ static const struct plugin_api rockbox_api = {
/* new stuff at the end, sort into place next time /* new stuff at the end, sort into place next time
the API gets incompatible */ the API gets incompatible */
lcd_putsf,
}; };
int plugin_load(const char* plugin, const void* parameter) int plugin_load(const char* plugin, const void* parameter)

View file

@ -894,6 +894,8 @@ struct plugin_api {
/* new stuff at the end, sort into place next time /* new stuff at the end, sort into place next time
the API gets incompatible */ the API gets incompatible */
void (*lcd_putsf)(int x, int y, const unsigned char *fmt, ...);
}; };
/* plugin header */ /* plugin header */

View file

@ -5,7 +5,7 @@
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/ * \/ \/ \/ \/ \/
* $Id$ * $Id$
* *
* Copyright (C) 2008 Lechner Michael / smoking gnu * Copyright (C) 2008 Lechner Michael / smoking gnu
* *
@ -19,15 +19,15 @@
* *
* INTRODUCTION: * INTRODUCTION:
* OK, this is an attempt to write an instrument tuner for rockbox. * OK, this is an attempt to write an instrument tuner for rockbox.
* It uses a Schmitt trigger algorithm, which I copied from * It uses a Schmitt trigger algorithm, which I copied from
* tuneit [ (c) 2004 Mario Lang <mlang@delysid.org> ], for detecting the * tuneit [ (c) 2004 Mario Lang <mlang@delysid.org> ], for detecting the
* fundamental freqency of a sound. A FFT algorithm would be more accurate * fundamental freqency of a sound. A FFT algorithm would be more accurate
* but also much slower. * but also much slower.
* *
* TODO: * TODO:
* - Adapt the Yin FFT algorithm, which would reduce complexity from O(n^2) * - Adapt the Yin FFT algorithm, which would reduce complexity from O(n^2)
* to O(nlogn), theoretically reducing latency by a factor of ~10. -David * to O(nlogn), theoretically reducing latency by a factor of ~10. -David
* *
* MAJOR CHANGES: * MAJOR CHANGES:
* 08.03.2008 Started coding * 08.03.2008 Started coding
* 21.03.2008 Pitch detection works more or less * 21.03.2008 Pitch detection works more or less
@ -42,7 +42,7 @@
* Aubio sound processing library (aubio.org). -David * Aubio sound processing library (aubio.org). -David
* 08.31.2009 Lots of changes: * 08.31.2009 Lots of changes:
* Added a menu to tweak settings * Added a menu to tweak settings
* Converted everything to fixed point (greatly improving * Converted everything to fixed point (greatly improving
* latency) * latency)
* Improved the display * Improved the display
* Improved efficiency with judicious use of cpu_boost, the * Improved efficiency with judicious use of cpu_boost, the
@ -51,17 +51,17 @@
* Fixed a problem that caused an octave-off error * Fixed a problem that caused an octave-off error
* -David * -David
* 05.14.2010 Multibuffer continuous recording with two buffers * 05.14.2010 Multibuffer continuous recording with two buffers
* *
* *
* CURRENT LIMITATIONS: * CURRENT LIMITATIONS:
* - No gapless recording. Strictly speaking true gappless isn't possible, * - No gapless recording. Strictly speaking true gappless isn't possible,
* since the algorithm takes longer to calculate than the length of the * since the algorithm takes longer to calculate than the length of the
* sample, but latency could be improved a bit with proper use of the DMA * sample, but latency could be improved a bit with proper use of the DMA
* recording functions. * recording functions.
* - Due to how the Yin algorithm works, latency is higher for lower * - Due to how the Yin algorithm works, latency is higher for lower
* frequencies. * frequencies.
*/ */
#include "plugin.h" #include "plugin.h"
#include "lib/pluginlib_actions.h" #include "lib/pluginlib_actions.h"
#include "lib/picture.h" #include "lib/picture.h"
@ -140,7 +140,7 @@ typedef struct _fixed fixed;
/* there'll be one sample per second, or a latency of one second. */ /* there'll be one sample per second, or a latency of one second. */
/* Furthermore, the lowest detectable frequency will be about twice */ /* Furthermore, the lowest detectable frequency will be about twice */
/* the number of reads per second */ /* the number of reads per second */
/* If we ever switch to Yin FFT algorithm then this needs to be /* If we ever switch to Yin FFT algorithm then this needs to be
a power of 2 */ a power of 2 */
#define BUFFER_SIZE 4096 #define BUFFER_SIZE 4096
#define SAMPLE_SIZE 4096 #define SAMPLE_SIZE 4096
@ -150,7 +150,7 @@ typedef struct _fixed fixed;
#define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100))) #define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100)))
/* The threshold for the YIN algorithm */ /* The threshold for the YIN algorithm */
#define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */ #define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */
const fixed yin_threshold_table[] IDATA_ATTR = static const fixed yin_threshold_table[] IDATA_ATTR =
{ {
float2fixed(0.01), float2fixed(0.01),
float2fixed(0.02), float2fixed(0.02),
@ -173,14 +173,12 @@ const fixed yin_threshold_table[] IDATA_ATTR =
* the note. The frequency is scaled in a way that the main * the note. The frequency is scaled in a way that the main
* algorithm can assume the frequency of A to be 440 Hz. * algorithm can assume the frequency of A to be 440 Hz.
*/ */
struct freq_A_entry static const struct
{ {
const int frequency; /* Frequency in Hz */ const int frequency; /* Frequency in Hz */
const fixed ratio; /* 440/frequency */ const fixed ratio; /* 440/frequency */
const fixed logratio; /* log2(factor) */ const fixed logratio; /* log2(factor) */
}; } freq_A[] =
const struct freq_A_entry freq_A[] =
{ {
{435, float2fixed(1.011363636), float2fixed( 0.016301812)}, {435, float2fixed(1.011363636), float2fixed( 0.016301812)},
{436, float2fixed(1.009090909), float2fixed( 0.013056153)}, {436, float2fixed(1.009090909), float2fixed( 0.013056153)},
@ -214,8 +212,8 @@ const struct freq_A_entry freq_A[] =
#define DISPLAY_HZ_PRECISION 100 #define DISPLAY_HZ_PRECISION 100
/* Where to put the various GUI elements */ /* Where to put the various GUI elements */
int note_y; static int note_y;
int bar_grad_y; static int bar_grad_y;
#define LCD_RES_MIN (LCD_HEIGHT < LCD_WIDTH ? LCD_HEIGHT : LCD_WIDTH) #define LCD_RES_MIN (LCD_HEIGHT < LCD_WIDTH ? LCD_HEIGHT : LCD_WIDTH)
#define BAR_PADDING (LCD_RES_MIN / 32) #define BAR_PADDING (LCD_RES_MIN / 32)
#define BAR_Y (LCD_HEIGHT * 3 / 4) #define BAR_Y (LCD_HEIGHT * 3 / 4)
@ -225,7 +223,7 @@ int bar_grad_y;
#define HZ_Y 0 #define HZ_Y 0
#define GRADUATION 10 /* Subdivisions of the whole 100-cent scale */ #define GRADUATION 10 /* Subdivisions of the whole 100-cent scale */
/* Bitmaps for drawing the note names. These need to have height /* Bitmaps for drawing the note names. These need to have height
<= (bar_grad_y - note_y), or 15/32 * LCD_HEIGHT <= (bar_grad_y - note_y), or 15/32 * LCD_HEIGHT
*/ */
#define NUM_NOTE_IMAGES 9 #define NUM_NOTE_IMAGES 9
@ -238,7 +236,8 @@ int bar_grad_y;
#define NOTE_INDEX_G 6 #define NOTE_INDEX_G 6
#define NOTE_INDEX_SHARP 7 #define NOTE_INDEX_SHARP 7
#define NOTE_INDEX_FLAT 8 #define NOTE_INDEX_FLAT 8
const struct picture note_bitmaps =
static const struct picture note_bitmaps =
{ {
pitch_notes, pitch_notes,
BMPWIDTH_pitch_notes, BMPWIDTH_pitch_notes,
@ -261,16 +260,13 @@ static int16_t iram_audio_data[BUFFER_SIZE] IBSS_ATTR;
#endif #endif
#endif #endif
/* Description of a note of scale */ /* Notes within one (reference) scale */
struct note_entry static const struct
{ {
const char *name; /* Name of the note, e.g. "A#" */ const char *name; /* Name of the note, e.g. "A#" */
const fixed freq; /* Note frequency, Hz */ const fixed freq; /* Note frequency, Hz */
const fixed logfreq; /* log2(frequency) */ const fixed logfreq; /* log2(frequency) */
}; } notes[] =
/* Notes within one (reference) scale */
static const struct note_entry notes[] =
{ {
{"A" , float2fixed(440.0000000f), float2fixed(8.781359714f)}, {"A" , float2fixed(440.0000000f), float2fixed(8.781359714f)},
{"A#", float2fixed(466.1637615f), float2fixed(8.864693047f)}, {"A#", float2fixed(466.1637615f), float2fixed(8.864693047f)},
@ -295,7 +291,7 @@ static int bar_x_0;
static int lbl_x_minus_50, lbl_x_minus_20, lbl_x_0, lbl_x_20, lbl_x_50; static int lbl_x_minus_50, lbl_x_minus_20, lbl_x_0, lbl_x_20, lbl_x_50;
/* Settings for the plugin */ /* Settings for the plugin */
struct tuner_settings static struct tuner_settings
{ {
unsigned volume_threshold; unsigned volume_threshold;
unsigned record_gain; unsigned record_gain;
@ -305,7 +301,7 @@ struct tuner_settings
int freq_A; /* Index of the frequency of A */ int freq_A; /* Index of the frequency of A */
bool use_sharps; bool use_sharps;
bool display_hz; bool display_hz;
} tuner_settings; } settings;
/*=================================================================*/ /*=================================================================*/
/* Settings loading and saving(adapted from the clock plugin) */ /* Settings loading and saving(adapted from the clock plugin) */
@ -313,98 +309,68 @@ struct tuner_settings
#define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings" #define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings"
enum message /* The settings as they exist on the hard disk, so that
{
MESSAGE_LOADING,
MESSAGE_LOADED,
MESSAGE_ERRLOAD,
MESSAGE_SAVING,
MESSAGE_SAVED,
MESSAGE_ERRSAVE
};
enum settings_file_status
{
LOADED, ERRLOAD,
SAVED, ERRSAVE
};
/* The settings as they exist on the hard disk, so that
* we can know at saving time if changes have been made */ * we can know at saving time if changes have been made */
struct tuner_settings hdd_tuner_settings; static struct tuner_settings hdd_settings;
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
bool settings_needs_saving(struct tuner_settings* settings) static bool settings_needs_saving(void)
{ {
return(rb->memcmp(settings, &hdd_tuner_settings, sizeof(*settings))); return(rb->memcmp(&settings, &hdd_settings, sizeof(settings)));
} }
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
void tuner_settings_reset(struct tuner_settings* settings) static void tuner_settings_reset(void)
{ {
settings->volume_threshold = VOLUME_THRESHOLD; settings = (struct tuner_settings) {
settings->record_gain = rb->global_settings->rec_mic_gain; .volume_threshold = VOLUME_THRESHOLD,
settings->sample_size = BUFFER_SIZE; .record_gain = rb->global_settings->rec_mic_gain,
settings->lowest_freq = period2freq(BUFFER_SIZE / 4); .sample_size = BUFFER_SIZE,
settings->yin_threshold = DEFAULT_YIN_THRESHOLD; .lowest_freq = period2freq(BUFFER_SIZE / 4),
settings->freq_A = DEFAULT_FREQ_A; .yin_threshold = DEFAULT_YIN_THRESHOLD,
settings->use_sharps = true; .freq_A = DEFAULT_FREQ_A,
settings->display_hz = false; .use_sharps = true,
.display_hz = false,
};
} }
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
enum settings_file_status tuner_settings_load(struct tuner_settings* settings, static void load_settings(void)
char* filename)
{ {
int fd = rb->open(filename, O_RDONLY); int fd = rb->open(SETTINGS_FILENAME, O_RDONLY);
if(fd >= 0){ /* does file exist? */ if(fd < 0){ /* file doesn't exist */
/* basic consistency check */ /* Initializes the settings with default values at least */
if(rb->filesize(fd) == sizeof(*settings)){ tuner_settings_reset();
rb->read(fd, settings, sizeof(*settings)); return;
rb->close(fd);
rb->memcpy(&hdd_tuner_settings, settings, sizeof(*settings));
return(LOADED);
}
} }
/* Initializes the settings with default values at least */
tuner_settings_reset(settings);
return(ERRLOAD);
}
/*---------------------------------------------------------------------*/ /* basic consistency check */
if(rb->filesize(fd) == sizeof(settings)){
enum settings_file_status tuner_settings_save(struct tuner_settings* settings, rb->read(fd, &settings, sizeof(settings));
char* filename) rb->memcpy(&hdd_settings, &settings, sizeof(settings));
{
int fd = rb->creat(filename, 0666);
if(fd >= 0){ /* does file exist? */
rb->write (fd, settings, sizeof(*settings));
rb->close(fd);
return(SAVED);
} }
return(ERRSAVE); else{
tuner_settings_reset();
}
rb->close(fd);
} }
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
void load_settings(void) static void save_settings(void)
{ {
tuner_settings_load(&tuner_settings, SETTINGS_FILENAME); if(!settings_needs_saving())
rb->storage_sleep();
}
/*---------------------------------------------------------------------*/
void save_settings(void)
{
if(!settings_needs_saving(&tuner_settings))
return; return;
tuner_settings_save(&tuner_settings, SETTINGS_FILENAME); int fd = rb->creat(SETTINGS_FILENAME, 0666);
if(fd >= 0){ /* does file exist? */
rb->write (fd, &settings, sizeof(settings));
rb->close(fd);
}
} }
/*=================================================================*/ /*=================================================================*/
@ -423,7 +389,7 @@ const struct button_mapping* plugin_contexts[]={
/* Option strings */ /* Option strings */
/* This has to match yin_threshold_table */ /* This has to match yin_threshold_table */
static const struct opt_items yin_threshold_text[] = static const struct opt_items yin_threshold_text[] =
{ {
{ "0.01", -1 }, { "0.01", -1 },
{ "0.02", -1 }, { "0.02", -1 },
@ -441,27 +407,27 @@ static const struct opt_items yin_threshold_text[] =
{ "0.50", -1 }, { "0.50", -1 },
}; };
static const struct opt_items accidental_text[] = static const struct opt_items accidental_text[] =
{ {
{ "Flat", -1 }, { "Flat", -1 },
{ "Sharp", -1 }, { "Sharp", -1 },
}; };
void set_min_freq(int new_freq) static void set_min_freq(int new_freq)
{ {
tuner_settings.sample_size = freq2period(new_freq) * 4; settings.sample_size = freq2period(new_freq) * 4;
/* clamp the sample size between min and max */ /* clamp the sample size between min and max */
if(tuner_settings.sample_size <= SAMPLE_SIZE_MIN) if(settings.sample_size <= SAMPLE_SIZE_MIN)
tuner_settings.sample_size = SAMPLE_SIZE_MIN; settings.sample_size = SAMPLE_SIZE_MIN;
else if(tuner_settings.sample_size >= BUFFER_SIZE) else if(settings.sample_size >= BUFFER_SIZE)
tuner_settings.sample_size = BUFFER_SIZE; settings.sample_size = BUFFER_SIZE;
/* sample size must be divisible by 4 - round up */ /* sample size must be divisible by 4 - round up */
tuner_settings.sample_size = (tuner_settings.sample_size + 3) & ~3; settings.sample_size = (settings.sample_size + 3) & ~3;
} }
bool main_menu(void) static bool main_menu(void)
{ {
int selection = 0; int selection = 0;
bool done = false; bool done = false;
@ -494,58 +460,58 @@ bool main_menu(void)
{ {
case 1: case 1:
rb->set_int("Volume Threshold", "%", UNIT_INT, rb->set_int("Volume Threshold", "%", UNIT_INT,
&tuner_settings.volume_threshold, &settings.volume_threshold,
NULL, 5, 5, 95, NULL); NULL, 5, 5, 95, NULL);
break; break;
case 2: case 2:
rb->set_int("Listening Volume", "%", UNIT_INT, rb->set_int("Listening Volume", "%", UNIT_INT,
&tuner_settings.record_gain, &settings.record_gain,
NULL, 1, rb->sound_min(SOUND_MIC_GAIN), NULL, 1, rb->sound_min(SOUND_MIC_GAIN),
rb->sound_max(SOUND_MIC_GAIN), NULL); rb->sound_max(SOUND_MIC_GAIN), NULL);
break; break;
case 3: case 3:
rb->set_int("Lowest Frequency", "Hz", UNIT_INT, rb->set_int("Lowest Frequency", "Hz", UNIT_INT,
&tuner_settings.lowest_freq, set_min_freq, 1, &settings.lowest_freq, set_min_freq, 1,
/* Range depends on the size of the buffer */ /* Range depends on the size of the buffer */
sample_rate / (BUFFER_SIZE / 4), sample_rate / (BUFFER_SIZE / 4),
sample_rate / (SAMPLE_SIZE_MIN / 4), NULL); sample_rate / (SAMPLE_SIZE_MIN / 4), NULL);
break; break;
case 4: case 4:
rb->set_option( rb->set_option(
"Algorithm Pickiness (Lower -> more discriminating)", "Algorithm Pickiness (Lower -> more discriminating)",
&tuner_settings.yin_threshold, &settings.yin_threshold,
INT, yin_threshold_text, INT, yin_threshold_text,
sizeof(yin_threshold_text) / sizeof(yin_threshold_text[0]), sizeof(yin_threshold_text) / sizeof(yin_threshold_text[0]),
NULL); NULL);
break; break;
case 5: case 5:
rb->set_option("Display Accidentals As", rb->set_option("Display Accidentals As",
&tuner_settings.use_sharps, &settings.use_sharps,
BOOL, accidental_text, 2, NULL); BOOL, accidental_text, 2, NULL);
break; break;
case 6: case 6:
rb->set_bool("Display Frequency (Hz)", rb->set_bool("Display Frequency (Hz)",
&tuner_settings.display_hz); &settings.display_hz);
break; break;
case 7: case 7:
freq_val = freq_A[tuner_settings.freq_A].frequency; freq_val = freq_A[settings.freq_A].frequency;
rb->set_int("Frequency of A (Hz)", rb->set_int("Frequency of A (Hz)",
"Hz", UNIT_INT, &freq_val, NULL, "Hz", UNIT_INT, &freq_val, NULL,
1, freq_A[0].frequency, freq_A[NUM_FREQ_A-1].frequency, 1, freq_A[0].frequency, freq_A[NUM_FREQ_A-1].frequency,
NULL); NULL);
tuner_settings.freq_A = freq_val - freq_A[0].frequency; settings.freq_A = freq_val - freq_A[0].frequency;
break; break;
case 8: case 8:
reset = false; reset = false;
rb->set_bool("Reset Tuner Settings?", &reset); rb->set_bool("Reset Tuner Settings?", &reset);
if (reset) if (reset)
tuner_settings_reset(&tuner_settings); tuner_settings_reset();
break; break;
case 9: case 9:
exit_tuner = true; exit_tuner = true;
done = true; done = true;
break; break;
case 0: case 0:
default: default:
/* Return to the tuner */ /* Return to the tuner */
done = true; done = true;
@ -562,15 +528,15 @@ bool main_menu(void)
/*=================================================================*/ /*=================================================================*/
/* Fixed-point log base 2*/ /* Fixed-point log base 2*/
/* Adapted from python code at /* Adapted from python code at
http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm
*/ */
fixed log(fixed inp) static fixed log(fixed inp)
{ {
fixed x = inp; fixed x = inp;
fixed fp = int2fixed(1); fixed fp = int2fixed(1);
fixed res = int2fixed(0); fixed res = int2fixed(0);
if(fp_lte(x, FP_ZERO)) if(fp_lte(x, FP_ZERO))
{ {
return FP_MIN; return FP_MIN;
@ -611,59 +577,25 @@ fixed log(fixed inp)
/* GUI Stuff */ /* GUI Stuff */
/*=================================================================*/ /*=================================================================*/
/* The function name is pretty self-explaining ;) */
void print_int_xy(int x, int y, int v)
{
char temp[20];
#if LCD_DEPTH > 1
rb->lcd_set_foreground(front_color);
#endif
rb->snprintf(temp,20,"%d",v);
rb->lcd_putsxy(x,y,temp);
}
/* Print out the frequency etc */
void print_str(char* s)
{
#if LCD_DEPTH > 1
rb->lcd_set_foreground(front_color);
#endif
rb->lcd_putsxy(0, HZ_Y, s);
}
/* What can I say? Read the function name... */
void print_char_xy(int x, int y, char c)
{
char temp[2];
temp[0]=c;
temp[1]=0;
#if LCD_DEPTH > 1
rb->lcd_set_foreground(front_color);
#endif
rb->lcd_putsxy(x, y, temp);
}
/* Draw the note bitmap */ /* Draw the note bitmap */
void draw_note(const char *note) static void draw_note(const char *note)
{ {
int i; int i;
int note_x = (LCD_WIDTH - BMPWIDTH_pitch_notes) / 2; int note_x = (LCD_WIDTH - BMPWIDTH_pitch_notes) / 2;
int accidental_index = NOTE_INDEX_SHARP; int accidental_index = NOTE_INDEX_SHARP;
i = note[0]-'A'; i = note[0]-'A';
if(note[1] == '#') if(note[1] == '#')
{ {
if(!(tuner_settings.use_sharps)) if(!(settings.use_sharps))
{ {
i = (i + 1) % 7; i = (i + 1) % 7;
accidental_index = NOTE_INDEX_FLAT; accidental_index = NOTE_INDEX_FLAT;
} }
vertical_picture_draw_sprite(rb->screens[0], vertical_picture_draw_sprite(rb->screens[0],
&note_bitmaps, &note_bitmaps,
accidental_index, accidental_index,
LCD_WIDTH / 2, LCD_WIDTH / 2,
note_y); note_y);
@ -674,9 +606,10 @@ void draw_note(const char *note)
note_x, note_x,
note_y); note_y);
} }
/* Draw the red bar and the white lines */ /* Draw the red bar and the white lines */
void draw_bar(fixed wrong_by_cents) static void draw_bar(fixed wrong_by_cents)
{ {
unsigned n; unsigned n;
int x; int x;
@ -698,11 +631,14 @@ void draw_bar(fixed wrong_by_cents)
rb->lcd_vline(x, BAR_HLINE_Y, BAR_HLINE_Y2); rb->lcd_vline(x, BAR_HLINE_Y, BAR_HLINE_Y2);
} }
print_int_xy(lbl_x_minus_50 ,bar_grad_y, -50); #if LCD_DEPTH > 1
print_int_xy(lbl_x_minus_20 ,bar_grad_y, -20); rb->lcd_set_foreground(front_color);
print_int_xy(lbl_x_0 ,bar_grad_y, 0); #endif
print_int_xy(lbl_x_20 ,bar_grad_y, 20); rb->lcd_putsf(lbl_x_minus_50 ,bar_grad_y, "%d", -50);
print_int_xy(lbl_x_50 ,bar_grad_y, 50); rb->lcd_putsf(lbl_x_minus_20 ,bar_grad_y, "%d", -20);
rb->lcd_putsf(lbl_x_0 ,bar_grad_y, "%d", 0);
rb->lcd_putsf(lbl_x_20 ,bar_grad_y, "%d", 20);
rb->lcd_putsf(lbl_x_50 ,bar_grad_y, "%d", 50);
#ifdef HAVE_LCD_COLOR #ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */ rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */
@ -712,26 +648,25 @@ void draw_bar(fixed wrong_by_cents)
if (fp_gt(wrong_by_cents, FP_ZERO)) if (fp_gt(wrong_by_cents, FP_ZERO))
{ {
rb->lcd_fillrect(bar_x_0, BAR_Y, rb->lcd_fillrect(bar_x_0, BAR_Y,
fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), BAR_HEIGHT); fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), BAR_HEIGHT);
} }
else else
{ {
rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)), rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)),
BAR_Y, BAR_Y,
fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1, fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1,
BAR_HEIGHT); BAR_HEIGHT);
} }
} }
/* Calculate how wrong the note is and draw the GUI */ /* Calculate how wrong the note is and draw the GUI */
void display_frequency (fixed freq) static void display_frequency (fixed freq)
{ {
fixed ldf, mldf; fixed ldf, mldf;
fixed lfreq, nfreq; fixed lfreq, nfreq;
fixed orig_freq; fixed orig_freq;
int i, note = 0; int i, note = 0;
char str_buf[30];
if (fp_lt(freq, FP_LOW)) if (fp_lt(freq, FP_LOW))
freq = FP_LOW; freq = FP_LOW;
@ -739,8 +674,8 @@ void display_frequency (fixed freq)
/* We calculate the frequency and its log as if */ /* We calculate the frequency and its log as if */
/* the reference frequency of A were 440 Hz. */ /* the reference frequency of A were 440 Hz. */
orig_freq = freq; orig_freq = freq;
lfreq = fp_add(log(freq), freq_A[tuner_settings.freq_A].logratio); lfreq = fp_add(log(freq), freq_A[settings.freq_A].logratio);
freq = fp_mul(freq, freq_A[tuner_settings.freq_A].ratio); freq = fp_mul(freq, freq_A[settings.freq_A].ratio);
/* This calculates a log freq offset for note A */ /* This calculates a log freq offset for note A */
/* Get the frequency to within the range of our reference table, */ /* Get the frequency to within the range of our reference table, */
@ -775,39 +710,41 @@ void display_frequency (fixed freq)
if(fp_round(freq) != 0) if(fp_round(freq) != 0)
{ {
draw_note(notes[note].name); draw_note(notes[note].name);
if(tuner_settings.display_hz) if(settings.display_hz)
{ {
rb->snprintf(str_buf,30, "%s : %d cents (%d.%02dHz)", #if LCD_DEPTH > 1
rb->lcd_set_foreground(front_color);
#endif
rb->lcd_putsf(0, HZ_Y, "%s : %d cents (%d.%02dHz)",
notes[note].name, fp_round(ldf) ,fixed2int(orig_freq), notes[note].name, fp_round(ldf) ,fixed2int(orig_freq),
fp_round(fp_mul(fp_frac(orig_freq), fp_round(fp_mul(fp_frac(orig_freq),
int2fixed(DISPLAY_HZ_PRECISION)))); int2fixed(DISPLAY_HZ_PRECISION))));
print_str(str_buf);
} }
} }
rb->lcd_update(); rb->lcd_update();
} }
/*----------------------------------------------------------------------- /*-----------------------------------------------------------------------
* Functions for the Yin algorithm * Functions for the Yin algorithm
* *
* These were all adapted from the versions in Aubio v0.3.2 * These were all adapted from the versions in Aubio v0.3.2
* Here's what the Aubio documentation has to say: * Here's what the Aubio documentation has to say:
* *
* This algorithm was developped by A. de Cheveigne and H. Kawahara and * This algorithm was developped by A. de Cheveigne and H. Kawahara and
* published in: * published in:
* *
* de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency * de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency
* estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930. * estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930.
* *
* see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html * see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
/* Find the index of the minimum element of an array of floats */ /* Find the index of the minimum element of an array of floats */
unsigned vec_min_elem(fixed *s, unsigned buflen) static unsigned vec_min_elem(fixed *s, unsigned buflen)
{ {
unsigned j, pos=0.0f; unsigned j, pos=0.0f;
fixed tmp = s[0]; fixed tmp = s[0];
for (j=0; j < buflen; j++) for (j=0; j < buflen; j++)
{ {
if(fp_gt(tmp, s[j])) if(fp_gt(tmp, s[j]))
{ {
@ -819,13 +756,13 @@ unsigned vec_min_elem(fixed *s, unsigned buflen)
} }
static inline fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf) static inline fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf)
{ {
/* Original floating point version: */ /* Original floating point version: */
/* tmp = s0 + (pf/2.0f) * (pf * ( s0 - 2.0f*s1 + s2 ) - /* tmp = s0 + (pf/2.0f) * (pf * ( s0 - 2.0f*s1 + s2 ) -
3.0f*s0 + 4.0f*s1 - s2);*/ 3.0f*s0 + 4.0f*s1 - s2);*/
/* Converted to explicit operator precedence: */ /* Converted to explicit operator precedence: */
/* tmp = s0 + ((pf/2.0f) * ((((pf * ((s0 - (2*s1)) + s2)) - /* tmp = s0 + ((pf/2.0f) * ((((pf * ((s0 - (2*s1)) + s2)) -
(3*s0)) + (4*s1)) - s2)); */ (3*s0)) + (4*s1)) - s2)); */
/* I made it look like this so I could easily track the precedence and */ /* I made it look like this so I could easily track the precedence and */
@ -853,7 +790,7 @@ static inline fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf)
s0, s0,
fp_shl(s1, 1) fp_shl(s1, 1)
), ),
s2 s2
) )
), ),
fp_mul fp_mul
@ -873,32 +810,32 @@ static inline fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf)
#define QUADINT_STEP float2fixed(1.0f/200.0f) #define QUADINT_STEP float2fixed(1.0f/200.0f)
fixed ICODE_ATTR vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span) static fixed ICODE_ATTR vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span)
{ {
fixed res, frac, s0, s1, s2; fixed res, frac, s0, s1, s2;
fixed exactpos = int2fixed(pos); fixed exactpos = int2fixed(pos);
/* init resold to something big (in case x[pos+-span]<0)) */ /* init resold to something big (in case x[pos+-span]<0)) */
fixed resold = FP_MAX; fixed resold = FP_MAX;
if ((pos > span) && (pos < bufsize-span)) if ((pos > span) && (pos < bufsize-span))
{ {
s0 = x[pos-span]; s0 = x[pos-span];
s1 = x[pos] ; s1 = x[pos] ;
s2 = x[pos+span]; s2 = x[pos+span];
/* increase frac */ /* increase frac */
for (frac = float2fixed(0.0f); for (frac = float2fixed(0.0f);
fp_lt(frac, float2fixed(2.0f)); fp_lt(frac, float2fixed(2.0f));
frac = fp_add(frac, QUADINT_STEP)) frac = fp_add(frac, QUADINT_STEP))
{ {
res = aubio_quadfrac(s0, s1, s2, frac); res = aubio_quadfrac(s0, s1, s2, frac);
if (fp_lt(res, resold)) if (fp_lt(res, resold))
{ {
resold = res; resold = res;
} }
else else
{ {
/* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */ /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
exactpos = fp_add(exactpos, exactpos = fp_add(exactpos,
fp_sub( fp_sub(
fp_mul( fp_mul(
fp_sub(frac, QUADINT_STEP), fp_sub(frac, QUADINT_STEP),
@ -915,17 +852,17 @@ fixed ICODE_ATTR vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsig
} }
/* Calculate the period of the note in the /* Calculate the period of the note in the
buffer using the YIN algorithm */ buffer using the YIN algorithm */
/* The yin pointer is just a buffer that the algorithm uses as a work /* The yin pointer is just a buffer that the algorithm uses as a work
space. It needs to be half the length of the input buffer. */ space. It needs to be half the length of the input buffer. */
fixed ICODE_ATTR pitchyin(int16_t *input, fixed *yin) static fixed ICODE_ATTR pitchyin(int16_t *input, fixed *yin)
{ {
fixed retval; fixed retval;
unsigned j,tau = 0; unsigned j,tau = 0;
int period; int period;
unsigned yin_size = tuner_settings.sample_size / 4; unsigned yin_size = settings.sample_size / 4;
fixed tmp = FP_ZERO, tmp2 = FP_ZERO; fixed tmp = FP_ZERO, tmp2 = FP_ZERO;
yin[0] = int2fixed(1); yin[0] = int2fixed(1);
@ -934,7 +871,7 @@ fixed ICODE_ATTR pitchyin(int16_t *input, fixed *yin)
yin[tau] = FP_ZERO; yin[tau] = FP_ZERO;
for (j = 0; j < yin_size; j++) for (j = 0; j < yin_size; j++)
{ {
tmp = fp_sub(int2mantissa(input[2 * j]), tmp = fp_sub(int2mantissa(input[2 * j]),
int2mantissa(input[2 * (j + tau)])); int2mantissa(input[2 * (j + tau)]));
yin[tau] = fp_add(yin[tau], fp_mul(tmp, tmp)); yin[tau] = fp_add(yin[tau], fp_mul(tmp, tmp));
} }
@ -944,15 +881,15 @@ fixed ICODE_ATTR pitchyin(int16_t *input, fixed *yin)
yin[tau] = fp_mul(yin[tau], fp_div(int2fixed(tau), tmp2)); yin[tau] = fp_mul(yin[tau], fp_div(int2fixed(tau), tmp2));
} }
period = tau - 3; period = tau - 3;
if(tau > 4 && fp_lt(yin[period], if(tau > 4 && fp_lt(yin[period],
yin_threshold_table[tuner_settings.yin_threshold]) yin_threshold_table[settings.yin_threshold])
&& fp_lt(yin[period], yin[period+1])) && fp_lt(yin[period], yin[period+1]))
{ {
retval = vec_quadint_min(yin, yin_size, period, 1); retval = vec_quadint_min(yin, yin_size, period, 1);
return retval; return retval;
} }
} }
retval = vec_quadint_min(yin, yin_size, retval = vec_quadint_min(yin, yin_size,
vec_min_elem(yin, yin_size), 1); vec_min_elem(yin, yin_size), 1);
return retval; return retval;
/*return FP_ZERO;*/ /*return FP_ZERO;*/
@ -960,11 +897,11 @@ fixed ICODE_ATTR pitchyin(int16_t *input, fixed *yin)
/*-----------------------------------------------------------------*/ /*-----------------------------------------------------------------*/
uint32_t ICODE_ATTR buffer_magnitude(int16_t *input) static uint32_t ICODE_ATTR buffer_magnitude(int16_t *input)
{ {
unsigned n; unsigned n;
uint64_t tally = 0; uint64_t tally = 0;
const unsigned size = tuner_settings.sample_size; const unsigned size = settings.sample_size;
/* Operate on only one channel of the stereo signal */ /* Operate on only one channel of the stereo signal */
for(n = 0; n < size; n+=2) for(n = 0; n < size; n+=2)
@ -982,7 +919,7 @@ uint32_t ICODE_ATTR buffer_magnitude(int16_t *input)
/* Stop the recording when the buffer is full */ /* Stop the recording when the buffer is full */
#ifndef SIMULATOR #ifndef SIMULATOR
void recording_callback(int status, void **start, size_t *size) static void recording_callback(int status, void **start, size_t *size)
{ {
int tail = audio_tail ^ 1; int tail = audio_tail ^ 1;
@ -1003,13 +940,13 @@ static void record_data(void)
{ {
#ifndef SIMULATOR #ifndef SIMULATOR
/* Always record full buffer, even if not required */ /* Always record full buffer, even if not required */
rb->pcm_record_data(recording_callback, audio_data[audio_tail], rb->pcm_record_data(recording_callback, audio_data[audio_tail],
BUFFER_SIZE * sizeof (int16_t)); BUFFER_SIZE * sizeof (int16_t));
#endif #endif
} }
/* The main program loop */ /* The main program loop */
void record_and_get_pitch(void) static void record_and_get_pitch(void)
{ {
int quit=0, button; int quit=0, button;
bool redraw = true; bool redraw = true;
@ -1029,18 +966,18 @@ void record_and_get_pitch(void)
record_data(); record_data();
while(!quit) while(!quit)
{ {
while (audio_head == audio_tail && !quit) /* wait for the buffer to be filled */ while (audio_head == audio_tail && !quit) /* wait for the buffer to be filled */
{ {
button=pluginlib_getaction(HZ/100, plugin_contexts, PLA_ARRAY_COUNT); button=pluginlib_getaction(HZ/100, plugin_contexts, PLA_ARRAY_COUNT);
switch(button) switch(button)
{ {
case PLA_EXIT: case PLA_EXIT:
quit=true; quit=true;
break; break;
case PLA_CANCEL: case PLA_CANCEL:
rb->pcm_stop_recording(); rb->pcm_stop_recording();
quit = main_menu() != 0; quit = main_menu() != 0;
@ -1050,17 +987,17 @@ void record_and_get_pitch(void)
record_data(); record_data();
} }
break; break;
break; break;
} }
} }
if(!quit) if(!quit)
{ {
#ifndef SIMULATOR #ifndef SIMULATOR
/* Only do the heavy lifting if the volume is high enough */ /* Only do the heavy lifting if the volume is high enough */
if(buffer_magnitude(audio_data[audio_head]) > if(buffer_magnitude(audio_data[audio_head]) >
sqr(tuner_settings.volume_threshold * sqr(settings.volume_threshold *
rb->sound_max(SOUND_MIC_GAIN))) rb->sound_max(SOUND_MIC_GAIN)))
{ {
waiting = false; waiting = false;
@ -1071,7 +1008,7 @@ void record_and_get_pitch(void)
#endif #endif
#ifdef PLUGIN_USE_IRAM #ifdef PLUGIN_USE_IRAM
rb->memcpy(iram_audio_data, audio_data[audio_head], rb->memcpy(iram_audio_data, audio_data[audio_head],
tuner_settings.sample_size * sizeof (int16_t)); settings.sample_size * sizeof (int16_t));
#endif #endif
/* This returns the period of the detected pitch in samples */ /* This returns the period of the detected pitch in samples */
period = pitchyin(iram_audio_data, yin_buffer); period = pitchyin(iram_audio_data, yin_buffer);
@ -1080,7 +1017,7 @@ void record_and_get_pitch(void)
{ {
display_frequency(fp_period2freq(period)); display_frequency(fp_period2freq(period));
} }
else else
{ {
display_frequency(FP_ZERO); display_frequency(FP_ZERO);
} }
@ -1115,7 +1052,7 @@ void record_and_get_pitch(void)
} }
/* Init recording, tuning, and GUI */ /* Init recording, tuning, and GUI */
void init_everything(void) static void init_everything(void)
{ {
/* Disable all talking before initializing IRAM */ /* Disable all talking before initializing IRAM */
rb->talk_disable(true); rb->talk_disable(true);
@ -1123,17 +1060,18 @@ void init_everything(void)
PLUGIN_IRAM_INIT(rb); PLUGIN_IRAM_INIT(rb);
load_settings(); load_settings();
rb->storage_sleep();
/* Stop all playback (if no IRAM, otherwise IRAM_INIT would have) */ /* Stop all playback (if no IRAM, otherwise IRAM_INIT would have) */
rb->plugin_get_audio_buffer(NULL); rb->plugin_get_audio_buffer(NULL);
/* --------- Init the audio recording ----------------- */ /* --------- Init the audio recording ----------------- */
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
rb->audio_set_input_source(INPUT_TYPE, SRCF_RECORDING); rb->audio_set_input_source(INPUT_TYPE, SRCF_RECORDING);
/* set to maximum gain */ /* set to maximum gain */
rb->audio_set_recording_gain(tuner_settings.record_gain, rb->audio_set_recording_gain(settings.record_gain,
tuner_settings.record_gain, settings.record_gain,
AUDIO_GAIN_MIC); AUDIO_GAIN_MIC);
/* Highest C on piano is approx 4.186 kHz, so we need just over /* Highest C on piano is approx 4.186 kHz, so we need just over
@ -1145,15 +1083,15 @@ void init_everything(void)
rb->pcm_init_recording(); rb->pcm_init_recording();
/* avoid divsion by zero */ /* avoid divsion by zero */
if(tuner_settings.lowest_freq == 0) if(settings.lowest_freq == 0)
tuner_settings.lowest_freq = period2freq(BUFFER_SIZE / 4); settings.lowest_freq = period2freq(BUFFER_SIZE / 4);
/* GUI */ /* GUI */
#if LCD_DEPTH > 1 #if LCD_DEPTH > 1
front_color = rb->lcd_get_foreground(); front_color = rb->lcd_get_foreground();
#endif #endif
rb->lcd_getstringsize("X", &font_w, &font_h); rb->lcd_getstringsize("X", &font_w, &font_h);
bar_x_0 = LCD_WIDTH / 2; bar_x_0 = LCD_WIDTH / 2;
lbl_x_minus_50 = 0; lbl_x_minus_50 = 0;
lbl_x_minus_20 = (LCD_WIDTH / 2) - lbl_x_minus_20 = (LCD_WIDTH / 2) -
@ -1174,10 +1112,10 @@ void init_everything(void)
enum plugin_status plugin_start(const void* parameter) NO_PROF_ATTR enum plugin_status plugin_start(const void* parameter) NO_PROF_ATTR
{ {
(void)parameter; (void)parameter;
init_everything(); init_everything();
record_and_get_pitch(); record_and_get_pitch();
save_settings(); save_settings();
return 0; return PLUGIN_OK;
} }