Fix some of the voice and beep bugs that people have noticed since my new pcmbuf code went in, reduce code duplication, and improve performance while I'm at it


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8775 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index c79b0d5..31b59f8 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -97,7 +97,7 @@
 static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
 static size_t pcmbuf_mix_used_bytes IDATA_ATTR;
 static size_t pcmbuf_watermark IDATA_ATTR;
-static size_t mixpos IDATA_ATTR = 0;
+static short *mixpos IDATA_ATTR;
 static bool low_latency_mode = false;
 
 /* Helpful macros for use in conditionals this assumes some of the above
@@ -805,13 +805,6 @@
     }
 }
 
-void pcmbuf_write_voice(size_t length)
-{
-    while (pcm_is_playing())
-        sleep(1);
-    pcm_play_data(NULL, &guardbuf[0], length);
-}
-
 bool pcmbuf_insert_buffer(const char *buf, size_t length)
 {
     if (!prepare_insert(length))
@@ -827,52 +820,63 @@
     return true;
 }
 
+/* Get a pointer to where to mix immediate audio */
+static inline short* get_mix_insert_pos(void) {
+    /* Give at least 1/8s clearance here */
+    size_t pcmbuf_mix_back_pos =
+        pcmbuf_unplayed_bytes - NATIVE_FREQUENCY * 4 / 8;
+    
+    if (audiobuffer_pos < pcmbuf_mix_back_pos)
+        return (short *)&audiobuffer[pcmbuf_size +
+            audiobuffer_pos - pcmbuf_mix_back_pos];
+    else
+        return (short *)&audiobuffer[audiobuffer_pos - pcmbuf_mix_back_pos];
+}
+
 /* Generates a constant square wave sound with a given frequency
    in Hertz for a duration in milliseconds. */
-void pcmbuf_beep(int frequency, int duration, int amplitude)
+void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
 {
-    unsigned int state = 0, count = 0;
+    unsigned int count = 0, i = 0;
+    bool state = false;
     unsigned int interval = NATIVE_FREQUENCY / frequency;
-    size_t pos;
-    short *buf = (short *)audiobuffer;
-    size_t bufsize = pcmbuf_size / 2;
+    short *buf;
+    short *pcmbuf_end = (short *)guardbuf;
+    bool playing = pcm_is_playing();
+    size_t samples = NATIVE_FREQUENCY / 1000 * duration;
     
-    /* FIXME: Should start playback. */
-    //if (pcmbuf_unplayed_bytes * 1000 < 4 * NATIVE_FREQUENCY * duration)
-    //    return ;
-
-    if (audiobuffer_pos < pcmbuf_unplayed_bytes)
-        pos = pcmbuf_size + audiobuffer_pos - pcmbuf_unplayed_bytes;
-    else
-        pos = audiobuffer_pos - pcmbuf_unplayed_bytes;
-    pos /= 2;
-
-    duration = NATIVE_FREQUENCY / 1000 * duration;
-    while (duration-- > 0)
+    if (playing) {
+        buf = get_mix_insert_pos();
+    } else {
+        buf = (short *)audiobuffer;
+    }
+    while (i++ < samples)
     {
+        long sample = *buf;
         if (state) {
-            buf[pos] = MIN(MAX(buf[pos] + amplitude, -32768), 32767);
-            if (++pos >= bufsize)
-                pos = 0;
-            buf[pos] = MIN(MAX(buf[pos] + amplitude, -32768), 32767);
+            *buf++ = MIN(MAX(sample + amplitude, -32768), 32767);
+            if (buf > pcmbuf_end)
+                buf = (short *)audiobuffer;
+            sample = *buf;
+            *buf++ = MIN(MAX(sample + amplitude, -32768), 32767);
         } else {
-            buf[pos] = MIN(MAX(buf[pos] - amplitude, -32768), 32767);
-            if (++pos >= bufsize)
-                pos = 0;
-            buf[pos] = MIN(MAX(buf[pos] - amplitude, -32768), 32767);
+            *buf++ = MIN(MAX(sample - amplitude, -32768), 32767);
+            if (buf > pcmbuf_end)
+                buf = (short *)audiobuffer;
+            sample = *buf;
+            *buf++ = MIN(MAX(sample - amplitude, -32768), 32767);
         }
         
         if (++count >= interval)
         {
             count = 0;
-            if (state)
-                state = 0;
-            else
-                state = 1;
+            state = !state;
         }
-        pos++;
-        if (pos >= bufsize)
-            pos = 0;
+        if (buf > pcmbuf_end)
+            buf = (short *)audiobuffer;
+    }
+    if (!playing) {
+        pcm_play_data(NULL, (unsigned char *)audiobuffer, samples * 4);
     }
 }
 
@@ -889,19 +893,14 @@
 
 void pcmbuf_reset_mixpos(void)
 {
+    mixpos = get_mix_insert_pos();
     pcmbuf_mix_used_bytes = 0;
-    if (audiobuffer_pos < pcmbuf_unplayed_bytes)
-        mixpos = pcmbuf_size + audiobuffer_pos - pcmbuf_unplayed_bytes;
-    else
-        mixpos = audiobuffer_pos - pcmbuf_unplayed_bytes;
-    mixpos /= 2;
 }
 
 void pcmbuf_mix(char *buf, size_t length)
 {
     short *ibuf = (short *)buf;
-    short *obuf = (short *)audiobuffer;
-    size_t bufsize = pcmbuf_size / 2;
+    short *pcmbuf_end = (short *)guardbuf;
 
     if (pcmbuf_mix_used_bytes == 0)
         pcmbuf_reset_mixpos();
@@ -910,12 +909,12 @@
     length /= 2;
 
     while (length-- > 0) {
-        obuf[mixpos] = MIN(MAX(obuf[mixpos]/4 + *ibuf, -32768), 32767);
-        
-        ibuf++;
-        mixpos++;
-        if (mixpos >= bufsize)
-            mixpos = 0;
+        long sample = *ibuf++;
+        sample += *mixpos >> 2;
+        *mixpos++ = MIN(MAX(sample, -32768), 32767);
+
+        if (mixpos >= pcmbuf_end)
+            mixpos = (short *)audiobuffer;
     }
 }
 
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 555c1bc..b659e8f 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -58,7 +58,6 @@
 void pcmbuf_set_low_latency(bool state);
 bool pcmbuf_insert_buffer(const char *buf, size_t length);
 void pcmbuf_write_complete(size_t length);
-void pcmbuf_write_voice(size_t length);
 void* pcmbuf_request_buffer(size_t length, size_t *realsize);
 void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix);
 bool pcmbuf_is_crossfade_enabled(void);
@@ -66,7 +65,7 @@
 
 int pcmbuf_usage(void);
 int pcmbuf_mix_usage(void);
-void pcmbuf_beep(int frequency, int duration, int amplitude);
+void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
 void pcmbuf_reset_mixpos(void);
 void pcmbuf_mix(char *buf, size_t length);