Use playback channel directly for peakmeters and plugins using peak calculation. Also, for now, don't allow mixer playback to overlap recording, even if full duplex works.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30119 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/plugin.c b/apps/plugin.c
index bfb7f0e..6b3d399 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -781,6 +781,7 @@
 #if CONFIG_CODEC == SWCODEC
     mixer_channel_status,
     mixer_channel_get_buffer,
+    mixer_channel_calculate_peaks,
 #endif
 };
 
diff --git a/apps/plugin.h b/apps/plugin.h
index aa39829..77c8e83 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -146,7 +146,7 @@
 #define PLUGIN_MAGIC 0x526F634B /* RocK */
 
 /* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 206
+#define PLUGIN_API_VERSION 207
 
 /* update this to latest version if a change to the api struct breaks
    backwards compatibility (and please take the opportunity to sort in any
@@ -911,6 +911,8 @@
 #if CONFIG_CODEC == SWCODEC
     enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel);
     void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, int *count);
+    void (*mixer_channel_calculate_peaks)(enum pcm_mixer_channel channel,
+                                          int *left, int *right);
 #endif
 };
 
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index 07bf1da..5eb43fa 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -826,7 +826,8 @@
             left = rb->mas_codec_readreg(0xC);
             right = rb->mas_codec_readreg(0xD);
 #elif (CONFIG_CODEC == SWCODEC)
-            rb->pcm_calculate_peaks(&left, &right);
+            rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK,
+                                              &left, &right);
 #endif
             if (osc.orientation == OSC_HORIZ)
                 anim_horizontal(left, right);
diff --git a/apps/plugins/starfield.c b/apps/plugins/starfield.c
index 5e832aa..e7bbb42 100644
--- a/apps/plugins/starfield.c
+++ b/apps/plugins/starfield.c
@@ -422,7 +422,8 @@
             /* Get the peaks. ( Borrowed from vu_meter ) */
 #if (CONFIG_CODEC == SWCODEC)
             int left_peak, right_peak;
-            rb->pcm_calculate_peaks(&left_peak, &right_peak);
+            rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK,
+                                              &left_peak, &right_peak);
 #else
             int left_peak = rb->mas_codec_readreg(0xC);
             int right_peak = rb->mas_codec_readreg(0xD);
diff --git a/apps/plugins/vu_meter.c b/apps/plugins/vu_meter.c
index 5266214..cb1a035 100644
--- a/apps/plugins/vu_meter.c
+++ b/apps/plugins/vu_meter.c
@@ -660,7 +660,8 @@
     int right_peak = rb->mas_codec_readreg(0xD);
 #elif (CONFIG_CODEC == SWCODEC)
     int left_peak, right_peak;
-    rb->pcm_calculate_peaks(&left_peak, &right_peak);
+    rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK,
+                                      &left_peak, &right_peak);
 #endif
 
     if(vumeter_settings.analog_use_db_scale) {
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c
index c13c4c9..fd8f8d9 100644
--- a/apps/recorder/peakmeter.c
+++ b/apps/recorder/peakmeter.c
@@ -44,6 +44,7 @@
 
 #if CONFIG_CODEC == SWCODEC
 #include "pcm.h"
+#include "pcm_mixer.h"
 
 #ifdef HAVE_RECORDING
 #include "pcm_record.h"
@@ -620,7 +621,8 @@
    /* read current values */
 #if CONFIG_CODEC == SWCODEC
     if (pm_playback)
-        pcm_calculate_peaks(&pm_cur_left, &pm_cur_right);
+        mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK,
+                                      &pm_cur_left, &pm_cur_right);
 #ifdef HAVE_RECORDING
     else
         pcm_calculate_rec_peaks(&pm_cur_left, &pm_cur_right);
diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h
index d69138f..bae6a36 100644
--- a/firmware/export/pcm-internal.h
+++ b/firmware/export/pcm-internal.h
@@ -22,6 +22,16 @@
 #ifndef PCM_INTERNAL_H
 #define PCM_INTERNAL_H
 
+struct pcm_peaks
+{
+    long period;
+    long tick;
+    uint16_t val[2];
+};
+
+void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
+                             const void *addr, int count);
+
 /** The following are for internal use between pcm.c and target-
     specific portion **/
 
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
index 9c4e06e..be10601 100644
--- a/firmware/export/pcm_mixer.h
+++ b/firmware/export/pcm_mixer.h
@@ -103,6 +103,10 @@
 /* Return pointer to channel's playing audio data and the size remaining */
 void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count);
 
+/* Calculate peak values for channel */
+void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
+                                   int *left, int *right);
+
 /* Stop ALL channels and PCM and reset state */
 void mixer_reset(void);
 
diff --git a/firmware/pcm.c b/firmware/pcm.c
index b7415f3..c2ebc67 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -93,6 +93,9 @@
 /* samplerate frequency selection index */
 int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT;
 
+/* peak data for the global peak values - i.e. what the final output is */
+static struct pcm_peaks global_peaks;
+
 /* Called internally by functions to reset the state */
 static void pcm_play_stopped(void)
 {
@@ -107,7 +110,7 @@
  *
  * Used for recording and playback.
  */
-static void pcm_peak_peeker(const int32_t *addr, int count, int peaks[2])
+static void pcm_peak_peeker(const int32_t *addr, int count, uint16_t peaks[2])
 {
     int peak_l = 0, peak_r = 0;
     const int32_t * const end = addr + count;
@@ -145,18 +148,13 @@
     peaks[1] = peak_r;
 }
 
-void pcm_calculate_peaks(int *left, int *right)
+void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active,
+                             const void *addr, int count)
 {
-    static int peaks[2] = { 0, 0 };
-    static unsigned long last_peak_tick = 0;
-    static unsigned long frame_period   = 0;
-
     long tick = current_tick;
-    int count;
-    const void *addr;
 
-    /* Throttled peak ahead based on calling period */
-    long period = tick - last_peak_tick;
+    /* Peak no farther ahead than expected period to avoid overcalculation */
+    long period = tick - peaks->tick;
 
     /* Keep reasonable limits on period */
     if (period < 1)
@@ -164,33 +162,38 @@
     else if (period > HZ/5)
         period = HZ/5;
 
-    frame_period = (3*frame_period + period) >> 2;
+    peaks->period = (3*peaks->period + period) >> 2;
+    peaks->tick = tick;
 
-    last_peak_tick = tick;
-
-    addr = pcm_play_dma_get_peak_buffer(&count);
-
-    if (pcm_playing && !pcm_paused)
+    if (active)
     {
-        int framecount;
-
-        framecount = frame_period*pcm_curr_sampr / HZ;
+        int framecount = peaks->period*pcm_curr_sampr / HZ;
         count = MIN(framecount, count);
 
         if (count > 0)
-            pcm_peak_peeker((int32_t *)addr, count, peaks);
+            pcm_peak_peeker((int32_t *)addr, count, peaks->val);
         /* else keep previous peak values */
     }
     else
     {
-        peaks[0] = peaks[1] = 0;
+        /* peaks are zero */
+        peaks->val[0] = peaks->val[1] = 0;
     }
+}
+
+void pcm_calculate_peaks(int *left, int *right)
+{
+    int count;
+    const void *addr = pcm_play_dma_get_peak_buffer(&count);
+
+    pcm_do_peak_calculation(&global_peaks, pcm_playing && !pcm_paused,
+                            addr, count);
 
     if (left)
-        *left = peaks[0];
+        *left = global_peaks.val[0];
 
     if (right)
-        *right = peaks[1];
+        *right = global_peaks.val[1];
 }
 
 const void* pcm_get_peak_buffer(int * count)
@@ -437,7 +440,7 @@
  */
 void pcm_calculate_rec_peaks(int *left, int *right)
 {
-    static int peaks[2];
+    static uint16_t peaks[2];
 
     if (pcm_recording)
     {
@@ -484,10 +487,8 @@
 {
     logf("pcm_init_recording");
 
-#ifndef HAVE_PCM_FULL_DUPLEX
     /* Stop the beasty before attempting recording */
     mixer_reset();
-#endif
 
     /* Recording init is locked unlike general pcm init since this is not
      * just a one-time event at startup and it should and must be safe by
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
index 69a43cf..c847629 100644
--- a/firmware/pcm_mixer.c
+++ b/firmware/pcm_mixer.c
@@ -23,6 +23,7 @@
 #include "general.h"
 #include "kernel.h"
 #include "pcm.h"
+#include "pcm-internal.h"
 #include "pcm_mixer.h"
 #include "dsp.h"
 
@@ -59,6 +60,9 @@
 /* Descriptors for all available channels */
 static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR;
 
+/* History for channel peaks */
+static struct pcm_peaks channel_peaks[PCM_MIXER_NUM_CHANNELS];
+
 /* Packed pointer array of all playing (active) channels in "channels" array */
 static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR;
 
@@ -343,11 +347,14 @@
     if (pcm_is_playing())
         return;
 
-#if defined(HAVE_RECORDING) && !defined(HAVE_PCM_FULL_DUPLEX)
+#if defined(HAVE_RECORDING)
     if (pcm_is_recording())
         return;
 #endif
 
+    /* Requires a shared global sample rate for all channels */
+    pcm_set_frequency(NATIVE_FREQUENCY);
+
     /* Prepare initial frames and set up the double buffer */
     mixer_buffer_callback();
 
@@ -492,6 +499,25 @@
     return NULL;
 }
 
+/* Calculate peak values for channel */
+void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
+                                   int *left, int *right)
+{
+    struct mixer_channel *chan = &channels[channel];
+    struct pcm_peaks *peaks = &channel_peaks[channel];
+    int count;
+    const void *addr = mixer_channel_get_buffer(channel, &count);
+
+    pcm_do_peak_calculation(peaks, chan->status == CHANNEL_PLAYING,
+                            addr, count);
+
+    if (left)
+        *left = peaks->val[0];
+
+    if (right)
+        *right = peaks->val[1];
+}
+
 /* Stop ALL channels and PCM and reset state */
 void mixer_reset(void)
 {