pitch_detector: Use continuous recording, even if the algorithm is too slow for that and record a whole buffer even if the min frequency is higher. Use the minimum samplerate that allows C-4186 to be detected (usually 11.025kHz, which can reduce computational load to 1/16 compared to 44.1kHz). Get rid of 64-bit multiplies when calculating input RMS value. Stop audio playback when entering plugin. Better backlight and CPU frequency handling. audio_sample_type->int16_t. simpler buffer size rounding.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26005 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Michael Sevakis 2010-05-14 06:25:40 +00:00
parent 0b52d34313
commit 7250405c70
3 changed files with 107 additions and 77 deletions

View file

@ -713,6 +713,12 @@ static const struct plugin_api rockbox_api = {
#ifdef HAVE_LCD_BITMAP
is_diacritic,
#endif
#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && \
(defined(HAVE_LINE_IN) || defined(HAVE_MIC_IN))
round_value_to_list32,
#endif
};
int plugin_load(const char* plugin, const void* parameter)

View file

@ -142,7 +142,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
#define PLUGIN_API_VERSION 183
#define PLUGIN_API_VERSION 184
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@ -874,6 +874,14 @@ struct plugin_api {
#ifdef HAVE_LCD_BITMAP
bool (*is_diacritic)(const unsigned short char_code, bool *is_rtl);
#endif
#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && \
(defined(HAVE_LINE_IN) || defined(HAVE_MIC_IN))
int (*round_value_to_list32)(unsigned long value,
const unsigned long list[],
int count,
bool signd);
#endif
};
/* plugin header */

View file

@ -25,10 +25,6 @@
* but also much slower.
*
* TODO:
* - Find someone who knows how recording actually works, and rewrite the
* recording code to use proper, gapless recording with a callback function
* that provides new buffer, instead of stopping and restarting recording
* everytime the buffer is full
* - 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
*
@ -54,6 +50,7 @@
* calculation
* Fixed a problem that caused an octave-off error
* -David
* 05.14.2010 Multibuffer continuous recording with two buffers
*
*
* CURRENT LIMITATIONS:
@ -68,19 +65,11 @@
#include "plugin.h"
#include "lib/pluginlib_actions.h"
#include "lib/picture.h"
#include "lib/helper.h"
#include "pluginbitmaps/pitch_notes.h"
PLUGIN_HEADER
/* First figure out what sample rate we're going to use */
#if (REC_SAMPR_CAPS & SAMPR_CAP_44)
#define SAMPLE_RATE SAMPR_44
#elif (REC_SAMPR_CAPS & SAMPR_CAP_22)
#define SAMPLE_RATE SAMPR_22
#elif (REC_SAMPR_CAPS & SAMPR_CAP_11)
#define SAMPLE_RATE SAMPR_11
#endif
/* Some fixed point calculation stuff */
typedef int32_t fixed_data;
struct _fixed
@ -129,10 +118,10 @@ typedef struct _fixed fixed;
/* With an 18-bit decimal precision, the max value in the */
/* integer part is 8192. Divide 44100 by 7 and it'll fit in */
/* that variable. */
#define fp_period2freq(x) fp_div(int2fixed(SAMPLE_RATE / 7), \
#define fp_period2freq(x) fp_div(int2fixed(sample_rate / 7), \
fp_div((x),int2fixed(7)))
#define fp_freq2period(x) fp_period2freq(x)
#define period2freq(x) (SAMPLE_RATE / (x))
#define period2freq(x) (sample_rate / (x))
#define freq2period(x) period2freq(x)
#define sqr(x) ((x)*(x))
@ -146,7 +135,7 @@ typedef struct _fixed fixed;
/* The recording buffer size */
/* This is how much is sampled at a time. */
/* It also determines latency -- if BUFFER_SIZE == SAMPLE_RATE then */
/* It also determines latency -- if BUFFER_SIZE == sample_rate then */
/* there'll be one sample per second, or a latency of one second. */
/* Furthermore, the lowest detectable frequency will be about twice */
/* the number of reads per second */
@ -257,11 +246,12 @@ const struct picture note_bitmaps =
};
typedef signed short audio_sample_type;
static unsigned int sample_rate;
static int audio_head = 0; /* which of the two buffers to use? */
static volatile int audio_tail = 0; /* which of the two buffers to record? */
/* It's stereo, so make the buffer twice as big */
audio_sample_type audio_data[BUFFER_SIZE];
fixed yin_buffer[YIN_BUFFER_SIZE];
static int recording=0;
static int16_t audio_data[2][BUFFER_SIZE];
static fixed yin_buffer[YIN_BUFFER_SIZE];
/* Description of a note of scale */
struct note_entry
@ -460,9 +450,9 @@ void set_min_freq(int new_freq)
tuner_settings.sample_size = SAMPLE_SIZE_MIN;
else if(tuner_settings.sample_size >= BUFFER_SIZE)
tuner_settings.sample_size = BUFFER_SIZE;
/* sample size must be divisible by 4 */
else if(tuner_settings.sample_size % 4 != 0)
tuner_settings.sample_size += 4 - (tuner_settings.sample_size % 4);
/* sample size must be divisible by 4 - round up */
tuner_settings.sample_size = (tuner_settings.sample_size + 3) & ~3;
}
bool main_menu(void)
@ -474,6 +464,11 @@ bool main_menu(void)
int freq_val;
bool reset;
backlight_use_settings();
#ifdef HAVE_SCHEDULER_BOOSTCTRL
rb->cancel_cpu_boost();
#endif
MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL,
"Return to Tuner",
"Volume Threshold",
@ -506,8 +501,8 @@ bool main_menu(void)
rb->set_int("Lowest Frequency", "Hz", UNIT_INT,
&tuner_settings.lowest_freq, set_min_freq, 1,
/* Range depends on the size of the buffer */
SAMPLE_RATE / (BUFFER_SIZE / 4),
SAMPLE_RATE / (SAMPLE_SIZE_MIN / 4), NULL);
sample_rate / (BUFFER_SIZE / 4),
sample_rate / (SAMPLE_SIZE_MIN / 4), NULL);
break;
case 4:
rb->set_option(
@ -551,6 +546,8 @@ bool main_menu(void)
break;
}
}
backlight_force_on();
return exit_tuner;
}
@ -917,7 +914,7 @@ fixed vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span)
/* 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. */
fixed pitchyin(audio_sample_type *input, fixed *yin)
fixed pitchyin(int16_t *input, fixed *yin)
{
fixed retval;
unsigned j,tau = 0;
@ -957,7 +954,7 @@ fixed pitchyin(audio_sample_type *input, fixed *yin)
/*-----------------------------------------------------------------*/
uint32_t buffer_magnitude(audio_sample_type *input)
uint32_t buffer_magnitude(int16_t *input)
{
unsigned n;
uint64_t tally = 0;
@ -965,7 +962,8 @@ uint32_t buffer_magnitude(audio_sample_type *input)
/* Operate on only one channel of the stereo signal */
for(n = 0; n < tuner_settings.sample_size; n+=2)
{
tally += (uint64_t)input[n] * (uint64_t)input[n];
int s = input[n];
tally += s * s;
}
tally /= tuner_settings.sample_size / 2;
@ -976,12 +974,32 @@ uint32_t buffer_magnitude(audio_sample_type *input)
}
/* Stop the recording when the buffer is full */
#ifndef SIMULATOR
int recording_callback(int status)
{
(void) status;
recording=0;
return -1;
int tail = audio_tail ^ 1;
/* Do not overrun the reader. Reuse current buffer if full. */
if (tail != audio_head)
audio_tail = tail;
/* Always record full buffer, even if not required */
rb->pcm_record_more(audio_data[tail],
BUFFER_SIZE * sizeof (int16_t));
return 0;
(void)status;
}
#endif
/* Start recording */
static void record_data(void)
{
#ifndef SIMULATOR
/* Always record full buffer, even if not required */
rb->pcm_record_data(recording_callback, audio_data[audio_tail],
BUFFER_SIZE * sizeof (int16_t));
#endif
}
/* The main program loop */
@ -999,41 +1017,32 @@ void record_and_get_pitch(void)
bool waiting = false;
#endif
backlight_force_on();
record_data();
while(!quit)
{
#ifndef SIMULATOR
/* Start recording */
rb->pcm_record_data(recording_callback, (void *) audio_data,
(size_t) tuner_settings.sample_size *
sizeof(audio_sample_type));
#endif
recording=1;
while (recording && !quit) /* wait for the buffer to be filled */
while (audio_head == audio_tail && !quit) /* wait for the buffer to be filled */
{
rb->yield();
#ifdef SIMULATOR
/* Only do this loop once if this is the simulator */
recording = 0;
#endif
button=pluginlib_getaction(0, plugin_contexts, PLA_ARRAY_COUNT);
button=pluginlib_getaction(HZ/100, plugin_contexts, PLA_ARRAY_COUNT);
switch(button)
{
case PLA_QUIT:
quit=true;
rb->yield();
break;
case PLA_MENU:
if(main_menu())
quit=true;
else redraw = true;
rb->yield();
rb->pcm_stop_recording();
quit = main_menu() != 0;
if(!quit)
{
redraw = true;
record_data();
}
break;
default:
rb->yield();
break;
}
}
@ -1042,23 +1051,19 @@ void record_and_get_pitch(void)
{
#ifndef SIMULATOR
/* Only do the heavy lifting if the volume is high enough */
if(buffer_magnitude(audio_data) >
sqr(tuner_settings.volume_threshold *
if(buffer_magnitude(audio_data[audio_head]) >
sqr(tuner_settings.volume_threshold *
rb->sound_max(SOUND_MIC_GAIN)))
{
if(waiting)
{
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
waiting = false;
}
rb->backlight_on();
waiting = false;
redraw = false;
#ifdef HAVE_SCHEDULER_BOOSTCTRL
rb->trigger_cpu_boost();
#endif
/* This returns the period of the detected pitch in samples */
period = pitchyin(audio_data, yin_buffer);
period = pitchyin(audio_data[audio_head], yin_buffer);
/* Hz = sample rate / period */
if(fp_gt(period, FP_ZERO))
{
@ -1073,22 +1078,27 @@ void record_and_get_pitch(void)
{
waiting = true;
redraw = false;
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
/*rb->backlight_off();*/
display_frequency(FP_ZERO);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cancel_cpu_boost();
#endif
}
#else /* SIMULATOR */
/* Display a preselected frequency */
display_frequency(int2fixed(445));
#endif
/* Move to next buffer if not empty (but empty *shouldn't* happen
* here). */
if (audio_head != audio_tail)
audio_head ^= 1;
}
}
rb->pcm_close_recording();
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#ifdef HAVE_SCHEDULER_BOOSTCTRL
rb->cancel_cpu_boost();
#endif
backlight_use_settings();
}
/* Init recording, tuning, and GUI */
@ -1096,6 +1106,9 @@ void init_everything(void)
{
load_settings();
/* Stop all playback */
rb->plugin_get_audio_buffer(NULL);
/* --------- Init the audio recording ----------------- */
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
rb->audio_set_input_source(INPUT_TYPE, SRCF_RECORDING);
@ -1105,9 +1118,12 @@ void init_everything(void)
tuner_settings.record_gain,
AUDIO_GAIN_MIC);
rb->pcm_set_frequency(SAMPLE_RATE);
rb->pcm_apply_settings();
/* Highest C on piano is approx 4.186 kHz, so we need just over
* 8.372 kHz to pass it. */
sample_rate = rb->round_value_to_list32(9000, rb->rec_freq_sampr,
REC_NUM_FREQ, false);
sample_rate = rb->rec_freq_sampr[sample_rate];
rb->pcm_set_frequency(sample_rate);
rb->pcm_init_recording();
/* GUI */