forked from len0rd/rockbox
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
|
#ifdef HAVE_LCD_BITMAP
|
||||||
is_diacritic,
|
is_diacritic,
|
||||||
#endif
|
#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)
|
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 */
|
#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 183
|
#define PLUGIN_API_VERSION 184
|
||||||
|
|
||||||
/* 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
|
||||||
|
@ -874,6 +874,14 @@ struct plugin_api {
|
||||||
#ifdef HAVE_LCD_BITMAP
|
#ifdef HAVE_LCD_BITMAP
|
||||||
bool (*is_diacritic)(const unsigned short char_code, bool *is_rtl);
|
bool (*is_diacritic)(const unsigned short char_code, bool *is_rtl);
|
||||||
#endif
|
#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 */
|
/* plugin header */
|
||||||
|
|
|
@ -25,10 +25,6 @@
|
||||||
* but also much slower.
|
* but also much slower.
|
||||||
*
|
*
|
||||||
* TODO:
|
* 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)
|
* - 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
|
||||||
*
|
*
|
||||||
|
@ -54,6 +50,7 @@
|
||||||
* calculation
|
* calculation
|
||||||
* 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
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* CURRENT LIMITATIONS:
|
* CURRENT LIMITATIONS:
|
||||||
|
@ -68,19 +65,11 @@
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "lib/pluginlib_actions.h"
|
#include "lib/pluginlib_actions.h"
|
||||||
#include "lib/picture.h"
|
#include "lib/picture.h"
|
||||||
|
#include "lib/helper.h"
|
||||||
#include "pluginbitmaps/pitch_notes.h"
|
#include "pluginbitmaps/pitch_notes.h"
|
||||||
|
|
||||||
PLUGIN_HEADER
|
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 */
|
/* Some fixed point calculation stuff */
|
||||||
typedef int32_t fixed_data;
|
typedef int32_t fixed_data;
|
||||||
struct _fixed
|
struct _fixed
|
||||||
|
@ -129,10 +118,10 @@ typedef struct _fixed fixed;
|
||||||
/* With an 18-bit decimal precision, the max value in the */
|
/* With an 18-bit decimal precision, the max value in the */
|
||||||
/* integer part is 8192. Divide 44100 by 7 and it'll fit in */
|
/* integer part is 8192. Divide 44100 by 7 and it'll fit in */
|
||||||
/* that variable. */
|
/* 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)))
|
fp_div((x),int2fixed(7)))
|
||||||
#define fp_freq2period(x) fp_period2freq(x)
|
#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 freq2period(x) period2freq(x)
|
||||||
|
|
||||||
#define sqr(x) ((x)*(x))
|
#define sqr(x) ((x)*(x))
|
||||||
|
@ -146,7 +135,7 @@ typedef struct _fixed fixed;
|
||||||
|
|
||||||
/* The recording buffer size */
|
/* The recording buffer size */
|
||||||
/* This is how much is sampled at a time. */
|
/* 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. */
|
/* 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 */
|
||||||
|
@ -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 */
|
/* It's stereo, so make the buffer twice as big */
|
||||||
audio_sample_type audio_data[BUFFER_SIZE];
|
static int16_t audio_data[2][BUFFER_SIZE];
|
||||||
fixed yin_buffer[YIN_BUFFER_SIZE];
|
static fixed yin_buffer[YIN_BUFFER_SIZE];
|
||||||
static int recording=0;
|
|
||||||
|
|
||||||
/* Description of a note of scale */
|
/* Description of a note of scale */
|
||||||
struct note_entry
|
struct note_entry
|
||||||
|
@ -460,9 +450,9 @@ void set_min_freq(int new_freq)
|
||||||
tuner_settings.sample_size = SAMPLE_SIZE_MIN;
|
tuner_settings.sample_size = SAMPLE_SIZE_MIN;
|
||||||
else if(tuner_settings.sample_size >= BUFFER_SIZE)
|
else if(tuner_settings.sample_size >= BUFFER_SIZE)
|
||||||
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)
|
/* sample size must be divisible by 4 - round up */
|
||||||
tuner_settings.sample_size += 4 - (tuner_settings.sample_size % 4);
|
tuner_settings.sample_size = (tuner_settings.sample_size + 3) & ~3;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool main_menu(void)
|
bool main_menu(void)
|
||||||
|
@ -474,6 +464,11 @@ bool main_menu(void)
|
||||||
int freq_val;
|
int freq_val;
|
||||||
bool reset;
|
bool reset;
|
||||||
|
|
||||||
|
backlight_use_settings();
|
||||||
|
#ifdef HAVE_SCHEDULER_BOOSTCTRL
|
||||||
|
rb->cancel_cpu_boost();
|
||||||
|
#endif
|
||||||
|
|
||||||
MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL,
|
MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL,
|
||||||
"Return to Tuner",
|
"Return to Tuner",
|
||||||
"Volume Threshold",
|
"Volume Threshold",
|
||||||
|
@ -506,8 +501,8 @@ bool main_menu(void)
|
||||||
rb->set_int("Lowest Frequency", "Hz", UNIT_INT,
|
rb->set_int("Lowest Frequency", "Hz", UNIT_INT,
|
||||||
&tuner_settings.lowest_freq, set_min_freq, 1,
|
&tuner_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(
|
||||||
|
@ -551,6 +546,8 @@ bool main_menu(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backlight_force_on();
|
||||||
return exit_tuner;
|
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
|
/* 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 pitchyin(audio_sample_type *input, fixed *yin)
|
fixed pitchyin(int16_t *input, fixed *yin)
|
||||||
{
|
{
|
||||||
fixed retval;
|
fixed retval;
|
||||||
unsigned j,tau = 0;
|
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;
|
unsigned n;
|
||||||
uint64_t tally = 0;
|
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 */
|
/* Operate on only one channel of the stereo signal */
|
||||||
for(n = 0; n < tuner_settings.sample_size; n+=2)
|
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;
|
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 */
|
/* Stop the recording when the buffer is full */
|
||||||
|
#ifndef SIMULATOR
|
||||||
int recording_callback(int status)
|
int recording_callback(int status)
|
||||||
{
|
{
|
||||||
(void) status;
|
int tail = audio_tail ^ 1;
|
||||||
|
|
||||||
recording=0;
|
/* Do not overrun the reader. Reuse current buffer if full. */
|
||||||
return -1;
|
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 */
|
/* The main program loop */
|
||||||
|
@ -999,41 +1017,32 @@ void record_and_get_pitch(void)
|
||||||
bool waiting = false;
|
bool waiting = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
backlight_force_on();
|
||||||
|
|
||||||
|
record_data();
|
||||||
|
|
||||||
while(!quit)
|
while(!quit)
|
||||||
{
|
{
|
||||||
#ifndef SIMULATOR
|
while (audio_head == audio_tail && !quit) /* wait for the buffer to be filled */
|
||||||
/* 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 */
|
|
||||||
{
|
{
|
||||||
rb->yield();
|
button=pluginlib_getaction(HZ/100, plugin_contexts, PLA_ARRAY_COUNT);
|
||||||
#ifdef SIMULATOR
|
|
||||||
/* Only do this loop once if this is the simulator */
|
|
||||||
recording = 0;
|
|
||||||
#endif
|
|
||||||
button=pluginlib_getaction(0, plugin_contexts, PLA_ARRAY_COUNT);
|
|
||||||
switch(button)
|
switch(button)
|
||||||
{
|
{
|
||||||
case PLA_QUIT:
|
case PLA_QUIT:
|
||||||
quit=true;
|
quit=true;
|
||||||
rb->yield();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PLA_MENU:
|
case PLA_MENU:
|
||||||
if(main_menu())
|
rb->pcm_stop_recording();
|
||||||
quit=true;
|
quit = main_menu() != 0;
|
||||||
else redraw = true;
|
if(!quit)
|
||||||
rb->yield();
|
{
|
||||||
|
redraw = true;
|
||||||
|
record_data();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
rb->yield();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1042,23 +1051,19 @@ void record_and_get_pitch(void)
|
||||||
{
|
{
|
||||||
#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) >
|
if(buffer_magnitude(audio_data[audio_head]) >
|
||||||
sqr(tuner_settings.volume_threshold *
|
sqr(tuner_settings.volume_threshold *
|
||||||
rb->sound_max(SOUND_MIC_GAIN)))
|
rb->sound_max(SOUND_MIC_GAIN)))
|
||||||
{
|
{
|
||||||
if(waiting)
|
waiting = false;
|
||||||
{
|
|
||||||
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
||||||
rb->cpu_boost(true);
|
|
||||||
#endif
|
|
||||||
waiting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
rb->backlight_on();
|
|
||||||
redraw = false;
|
redraw = false;
|
||||||
|
|
||||||
|
#ifdef HAVE_SCHEDULER_BOOSTCTRL
|
||||||
|
rb->trigger_cpu_boost();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* This returns the period of the detected pitch in samples */
|
/* 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 */
|
/* Hz = sample rate / period */
|
||||||
if(fp_gt(period, FP_ZERO))
|
if(fp_gt(period, FP_ZERO))
|
||||||
{
|
{
|
||||||
|
@ -1073,22 +1078,27 @@ void record_and_get_pitch(void)
|
||||||
{
|
{
|
||||||
waiting = true;
|
waiting = true;
|
||||||
redraw = false;
|
redraw = false;
|
||||||
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
||||||
rb->cpu_boost(false);
|
|
||||||
#endif
|
|
||||||
/*rb->backlight_off();*/
|
|
||||||
display_frequency(FP_ZERO);
|
display_frequency(FP_ZERO);
|
||||||
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
||||||
|
rb->cancel_cpu_boost();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#else /* SIMULATOR */
|
#else /* SIMULATOR */
|
||||||
/* Display a preselected frequency */
|
/* Display a preselected frequency */
|
||||||
display_frequency(int2fixed(445));
|
display_frequency(int2fixed(445));
|
||||||
#endif
|
#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();
|
rb->pcm_close_recording();
|
||||||
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
#ifdef HAVE_SCHEDULER_BOOSTCTRL
|
||||||
rb->cpu_boost(false);
|
rb->cancel_cpu_boost();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
backlight_use_settings();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init recording, tuning, and GUI */
|
/* Init recording, tuning, and GUI */
|
||||||
|
@ -1096,6 +1106,9 @@ void init_everything(void)
|
||||||
{
|
{
|
||||||
load_settings();
|
load_settings();
|
||||||
|
|
||||||
|
/* Stop all playback */
|
||||||
|
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);
|
||||||
|
@ -1105,9 +1118,12 @@ void init_everything(void)
|
||||||
tuner_settings.record_gain,
|
tuner_settings.record_gain,
|
||||||
AUDIO_GAIN_MIC);
|
AUDIO_GAIN_MIC);
|
||||||
|
|
||||||
rb->pcm_set_frequency(SAMPLE_RATE);
|
/* Highest C on piano is approx 4.186 kHz, so we need just over
|
||||||
rb->pcm_apply_settings();
|
* 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();
|
rb->pcm_init_recording();
|
||||||
|
|
||||||
/* GUI */
|
/* GUI */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue