pitch_detector: Use continuous recording, even if the algorithm is too slow for that...
authorjethead71 <jethead71@a1c6a512-1295-4272-9138-f99709370657>
Fri, 14 May 2010 06:25:40 +0000 (14 06:25 +0000)
committerjethead71 <jethead71@a1c6a512-1295-4272-9138-f99709370657>
Fri, 14 May 2010 06:25:40 +0000 (14 06:25 +0000)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26005 a1c6a512-1295-4272-9138-f99709370657

apps/plugin.c
apps/plugin.h
apps/plugins/pitch_detector.c

index e4480de..a1a68af 100644 (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)
index e6e41e1..26d17a2 100644 (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 */
index a394450..9de8d99 100644 (file)
  * 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:
 #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 */