From 7250405c70a0d2fe8468c225f1e89840266e65c1 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Fri, 14 May 2010 06:25:40 +0000 Subject: [PATCH] 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 --- apps/plugin.c | 6 ++ apps/plugin.h | 10 +- apps/plugins/pitch_detector.c | 168 +++++++++++++++++++--------------- 3 files changed, 107 insertions(+), 77 deletions(-) diff --git a/apps/plugin.c b/apps/plugin.c index e4480deed1..a1a68af6ba 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -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) diff --git a/apps/plugin.h b/apps/plugin.h index e6e41e1a3f..26d17a2637 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -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 */ diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c index a394450290..9de8d991e3 100644 --- a/apps/plugins/pitch_detector.c +++ b/apps/plugins/pitch_detector.c @@ -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 */