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:
parent
0b52d34313
commit
7250405c70
3 changed files with 107 additions and 77 deletions
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue