Index: /trunk/src/VBox/Devices/Audio/pulse_stubs.c
===================================================================
--- /trunk/src/VBox/Devices/Audio/pulse_stubs.c	(revision 25677)
+++ /trunk/src/VBox/Devices/Audio/pulse_stubs.c	(revision 25678)
@@ -79,7 +79,10 @@
                 (pa_stream *s, pa_stream_notify_cb_t cb, void *userdata),
                 (s, cb, userdata))
-PROXY_STUB     (pa_stream_flush, pa_operation*,
+PROXY_STUB     (pa_stream_drain, pa_operation*,
                 (pa_stream *s, pa_stream_success_cb_t cb, void *userdata),
                 (s, cb, userdata))
+PROXY_STUB     (pa_stream_trigger, pa_operation*,
+                (pa_stream *s, pa_stream_success_cb_t cb, void *userdata),
+		(s, cb, userdata))
 PROXY_STUB     (pa_stream_new, pa_stream*,
                 (pa_context *c, const char *name, const pa_sample_spec *ss,
@@ -98,7 +101,4 @@
                 (pa_stream *p),
                 (p))
-PROXY_STUB     (pa_stream_trigger, pa_operation*,
-                (pa_stream *s, pa_stream_success_cb_t cb, void *userdata),
-                (s, cb, userdata))
 PROXY_STUB     (pa_stream_writable_size, size_t,
                 (pa_stream *p),
@@ -154,4 +154,7 @@
                 (m))
 PROXY_STUB     (pa_bytes_per_second, size_t,
+                (const pa_sample_spec *spec),
+                (spec))
+PROXY_STUB     (pa_frame_size, size_t,
                 (const pa_sample_spec *spec),
                 (spec))
@@ -168,7 +171,13 @@
                 (pa_operation *o),
                 (o))
+PROXY_STUB     (pa_operation_get_state, pa_operation_state_t,
+                (pa_operation *o),
+		(o))
 PROXY_STUB     (pa_strerror, const char*,
                 (int error),
                 (error))
+PROXY_STUB     (pa_stream_readable_size, size_t,
+                (pa_stream *p),
+		(p))
 
 
@@ -191,5 +200,6 @@
     ELEMENT(pa_stream_get_state),
     ELEMENT(pa_stream_set_state_callback),
-    ELEMENT(pa_stream_flush),
+    ELEMENT(pa_stream_drain),
+    ELEMENT(pa_stream_trigger),
     ELEMENT(pa_stream_new),
     ELEMENT(pa_stream_get_buffer_attr),
@@ -197,5 +207,4 @@
     ELEMENT(pa_stream_cork),
     ELEMENT(pa_stream_drop),
-    ELEMENT(pa_stream_trigger),
     ELEMENT(pa_stream_writable_size),
     ELEMENT(pa_context_connect),
@@ -216,9 +225,12 @@
     ELEMENT(pa_threaded_mainloop_lock),
     ELEMENT(pa_bytes_per_second),
+    ELEMENT(pa_frame_size),
     ELEMENT(pa_sample_format_to_string),
     ELEMENT(pa_sample_spec_valid),
     ELEMENT(pa_channel_map_init_auto),
     ELEMENT(pa_operation_unref),
+    ELEMENT(pa_operation_get_state),
     ELEMENT(pa_strerror),
+    ELEMENT(pa_stream_readable_size)
 };
 #undef ELEMENT
Index: /trunk/src/VBox/Devices/Audio/pulseaudio.c
===================================================================
--- /trunk/src/VBox/Devices/Audio/pulseaudio.c	(revision 25677)
+++ /trunk/src/VBox/Devices/Audio/pulseaudio.c	(revision 25678)
@@ -49,11 +49,23 @@
 typedef struct PulseVoice
 {
-    HWVoiceOut  hw;
-    void       *pPCMBuf;
-    pa_stream  *pStream;
-    int         fOpSuccess;
-    unsigned    cErrors;
+    HWVoiceOut     hw;
+    /** DAC buffer */
+    void           *pPCMBuf;
+    /** Pulse stream */
+    pa_stream      *pStream;
+    /** Pulse sample format and attribute specification */
+    pa_sample_spec SampleSpec;
+    /** Pulse playback and buffer metrics */
+    pa_buffer_attr BufAttr;
+    int            fOpSuccess;
+    /** number of logged errors */
+    unsigned       cErrors;
+    const uint8_t  *pu8PeekBuf;
+    size_t         cbPeekBuf;
+    size_t         offPeekBuf;
 } PulseVoice;
 
+/* The desired max buffer size in milliseconds. The desired latency will be
+ * calculated to be 1/10 of the value */
 static struct
 {
@@ -64,31 +76,6 @@
 {
     INIT_FIELD (.buffer_msecs_out = ) 100,
-    INIT_FIELD (.buffer_msecs_in  = ) 100,
+    INIT_FIELD (.buffer_msecs_in  = ) 300,
 };
-
-struct pulse_params_req
-{
-    int                 freq;
-    pa_sample_format_t  pa_format;
-    int                 nchannels;
-};
-
-struct pulse_params_obt
-{
-    int                 freq;
-    pa_sample_format_t  pa_format;
-    int                 nchannels;
-    unsigned long       buffer_size;
-};
-
-static void pulse_check_fatal (PulseVoice *pulse, int rc)
-{
-    if (rc == PA_ERR_CONNECTIONTERMINATED)
-    {
-        /* XXX runtime warning */
-        LogRel(("Pulse: Audio input/output stopped!\n"));
-        pulse->cErrors = MAX_LOG_REL_ERRORS;
-    }
-}
 
 static pa_sample_format_t aud_to_pulsefmt (audfmt_e fmt)
@@ -155,11 +142,18 @@
 static void context_state_callback(pa_context *c, void *userdata)
 {
+    PulseVoice *pPulse = (PulseVoice *)userdata;
     switch (pa_context_get_state(c))
     {
         case PA_CONTEXT_READY:
         case PA_CONTEXT_TERMINATED:
+            pa_threaded_mainloop_signal(g_pMainLoop, 0);
+            break;
+
         case PA_CONTEXT_FAILED:
+            LogRel(("Pulse: Audio input/output stopped!\n"));
+            pPulse->cErrors = MAX_LOG_REL_ERRORS;
             pa_threaded_mainloop_signal(g_pMainLoop, 0);
             break;
+
         default:
             break;
@@ -176,4 +170,5 @@
             pa_threaded_mainloop_signal(g_pMainLoop, 0);
             break;
+
         default:
             break;
@@ -181,21 +176,11 @@
 }
 
-static void stream_latency_update_callback(pa_stream *s, void *userdata)
-{
-    pa_threaded_mainloop_signal(g_pMainLoop, 0);
-}
-
-static int pulse_open (int fIn, struct pulse_params_req *req,
-                       struct pulse_params_obt *obt, pa_stream **ppStream)
-{
-    pa_sample_spec        sspec;
-    pa_channel_map        cmap;
+static int pulse_open (int fIn, pa_stream **ppStream, pa_sample_spec *pSampleSpec,
+                       pa_buffer_attr *pBufAttr)
+{
+    const pa_buffer_attr *pBufAttrObtained;
     pa_stream            *pStream = NULL;
-    pa_buffer_attr        bufAttr;
-    const pa_buffer_attr *pBufAttr;
-    const pa_sample_spec *pSampSpec;
-    char                  achPCMName[64];
-    pa_stream_flags_t     flags;
-    int                   ms = fIn ? conf.buffer_msecs_in : conf.buffer_msecs_out;
+    char                 achPCMName[64];
+    pa_stream_flags_t    flags = 0;
     const char           *stream_name = audio_get_stream_name();
 
@@ -205,13 +190,10 @@
                 fIn ? "pcm_in" : "pcm_out",
                 stream_name ? ")" : "");
-    sspec.rate     = req->freq;
-    sspec.channels = req->nchannels;
-    sspec.format   = req->pa_format;
 
     LogRel(("Pulse: open %s rate=%dHz channels=%d format=%s\n",
-                fIn ? "PCM_IN" : "PCM_OUT", req->freq, req->nchannels,
-                pa_sample_format_to_string(req->pa_format)));
-
-    if (!pa_sample_spec_valid(&sspec))
+                fIn ? "PCM_IN" : "PCM_OUT", pSampleSpec->rate, pSampleSpec->channels,
+                pa_sample_format_to_string(pSampleSpec->format)));
+
+    if (!pa_sample_spec_valid(pSampleSpec))
     {
         LogRel(("Pulse: Unsupported sample specification\n"));
@@ -219,13 +201,7 @@
     }
 
-    pa_channel_map_init_auto(&cmap, sspec.channels, PA_CHANNEL_MAP_ALSA);
-
-#if 0
-    pa_cvolume_reset(&volume, sspec.channels);
-#endif
-
     pa_threaded_mainloop_lock(g_pMainLoop);
 
-    if (!(pStream = pa_stream_new(g_pContext, achPCMName, &sspec, &cmap)))
+    if (!(pStream = pa_stream_new(g_pContext, achPCMName, pSampleSpec, /*channel_map=*/NULL)))
     {
         LogRel(("Pulse: Cannot create stream %s\n", achPCMName));
@@ -233,25 +209,22 @@
     }
 
-    pSampSpec      = pa_stream_get_sample_spec(pStream);
-    obt->pa_format = pSampSpec->format;
-    obt->nchannels = pSampSpec->channels;
-    obt->freq      = pSampSpec->rate;
-
     pa_stream_set_state_callback(pStream, stream_state_callback, NULL);
-    pa_stream_set_latency_update_callback(pStream, stream_latency_update_callback, NULL);
-
-    memset(&bufAttr, 0, sizeof(bufAttr));
-    bufAttr.tlength   = (pa_bytes_per_second(pSampSpec) * ms) / 1000;
-    bufAttr.maxlength = (bufAttr.tlength*3) / 2;
-    bufAttr.minreq    = pa_bytes_per_second(pSampSpec) / 100;    /* 10ms */
-    bufAttr.prebuf    = bufAttr.tlength - bufAttr.minreq;
-    bufAttr.fragsize  = pa_bytes_per_second(pSampSpec) / 100;    /* 10ms */
-
-    flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
+
+#if PA_API_VERSION >= 12
+    /* XXX */
+    flags |= PA_STREAM_ADJUST_LATENCY;
+#endif
+
     if (fIn)
     {
-        if (pa_stream_connect_record(pStream, /*dev=*/NULL, &bufAttr, flags) < 0)
-        {
-            LogRel(("Pulse: Cannot connect record stream : %s\n",
+        LogRel(("Pulse: Requested record buffer attributes: maxlength=%d fragsize=%d\n",
+                pBufAttr->maxlength, pBufAttr->fragsize));
+#if 0
+        /* not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time() */
+        flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
+#endif
+        if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, flags) < 0)
+        {
+            LogRel(("Pulse: Cannot connect record stream: %s\n",
                     pa_strerror(pa_context_errno(g_pContext))));
             goto disconnect_unlock_and_fail;
@@ -260,6 +233,11 @@
     else
     {
-        if (pa_stream_connect_playback(pStream, /*dev=*/NULL, &bufAttr, flags,
-                                       NULL, NULL) < 0)
+        LogRel(("Pulse: Requested playback buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
+                pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
+
+        flags |= PA_STREAM_START_CORKED;
+
+        if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, flags,
+                                       /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
         {
             LogRel(("Pulse: Cannot connect playback stream: %s\n",
@@ -274,4 +252,5 @@
         pa_stream_state_t sstate;
         pa_threaded_mainloop_wait(g_pMainLoop);
+
         sstate = pa_stream_get_state(pStream);
         if (sstate == PA_STREAM_READY)
@@ -284,12 +263,11 @@
     }
 
-    pBufAttr = pa_stream_get_buffer_attr(pStream);
-    obt->buffer_size = pBufAttr->maxlength;
+    pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
+    memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
+
+    LogRel(("Pulse: Obtained buffer attributes: tlength=%d maxlength=%d prebuf=%d minreq=%d fragsize=%d\n",
+            pBufAttr->tlength, pBufAttr->maxlength, pBufAttr->prebuf, pBufAttr->minreq, pBufAttr->fragsize));
 
     pa_threaded_mainloop_unlock(g_pMainLoop);
-
-    LogRel(("Pulse: buffer settings: max=%d tlength=%d prebuf=%d minreq=%d\n",
-            pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
-
     *ppStream = pStream;
     return 0;
@@ -311,39 +289,46 @@
 static int pulse_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
-    PulseVoice *pulse = (PulseVoice *) hw;
-    struct pulse_params_req req;
-    struct pulse_params_obt obt;
-    audfmt_e effective_fmt;
-    int endianness;
+    PulseVoice *pPulse = (PulseVoice *) hw;
     audsettings_t obt_as;
-
-    req.pa_format   = aud_to_pulsefmt (as->fmt);
-    req.freq        = as->freq;
-    req.nchannels   = as->nchannels;
-
-    if (pulse_open (/*fIn=*/0, &req, &obt, &pulse->pStream))
+    int cbBuf;
+
+    pPulse->SampleSpec.format   = aud_to_pulsefmt (as->fmt);
+    pPulse->SampleSpec.rate     = as->freq;
+    pPulse->SampleSpec.channels = as->nchannels;
+
+    /* Note that setting maxlength to -1 does not work for older PulseAudio libraries
+     * (at least not for 0.9.10). So use the suggested value of 3/2 of tlength */
+    pPulse->BufAttr.tlength     = (pa_bytes_per_second(&pPulse->SampleSpec)
+                                  * conf.buffer_msecs_out) / 1000;
+    pPulse->BufAttr.maxlength   = (pPulse->BufAttr.tlength * 3) / 2;
+    pPulse->BufAttr.prebuf      = -1; /* Same as tlength */
+    pPulse->BufAttr.minreq      = -1; /* Pulse should set something sensible for minreq on it's own */
+
+    /* Notice that the struct BufAttr is updated to the obtained values after this call */
+    if (pulse_open (0, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
         return -1;
 
-    if (pulse_to_audfmt (obt.pa_format, &effective_fmt, &endianness))
-    {
-        LogRel(("Pulse: Cannot find audio format %d\n", obt.pa_format));
+    if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
+    {
+        LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
         return -1;
     }
 
-    obt_as.freq       = obt.freq;
-    obt_as.nchannels  = obt.nchannels;
-    obt_as.fmt        = effective_fmt;
-    obt_as.endianness = endianness;
+    obt_as.freq       = pPulse->SampleSpec.rate;
+    obt_as.nchannels  = pPulse->SampleSpec.channels;
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = obt.buffer_size >> hw->info.shift;
-
-    pulse->pPCMBuf = RTMemAllocZ(obt.buffer_size);
-    if (!pulse->pPCMBuf)
-    {
-        LogRel(("Pulse: Could not allocate DAC buffer of %d bytes\n", obt.buffer_size));
+    cbBuf = audio_MIN(pPulse->BufAttr.tlength * 2, pPulse->BufAttr.maxlength);
+
+    pPulse->pPCMBuf = RTMemAllocZ(cbBuf);
+    if (!pPulse->pPCMBuf)
+    {
+        LogRel(("Pulse: Could not allocate DAC buffer of %d bytes\n", cbBuf));
         return -1;
     }
 
+    /* Convert from bytes to frames (aka samples) */
+    hw->samples = cbBuf >> hw->info.shift;
+
     return 0;
 }
@@ -351,15 +336,19 @@
 static void pulse_fini_out (HWVoiceOut *hw)
 {
-    PulseVoice *pulse = (PulseVoice *)hw;
-    if (pulse->pStream)
-    {
-        pa_stream_disconnect(pulse->pStream);
-        pa_stream_unref(pulse->pStream);
-        pulse->pStream = NULL;
-    }
-    if (pulse->pPCMBuf)
-    {
-        RTMemFree (pulse->pPCMBuf);
-        pulse->pPCMBuf = NULL;
+    PulseVoice *pPulse = (PulseVoice *)hw;
+
+    if (pPulse->pStream)
+    {
+        pa_threaded_mainloop_lock(g_pMainLoop);
+        pa_stream_disconnect(pPulse->pStream);
+        pa_stream_unref(pPulse->pStream);
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+        pPulse->pStream = NULL;
+    }
+
+    if (pPulse->pPCMBuf)
+    {
+        RTMemFree (pPulse->pPCMBuf);
+        pPulse->pPCMBuf = NULL;
     }
 }
@@ -367,53 +356,57 @@
 static int pulse_run_out (HWVoiceOut *hw)
 {
-    PulseVoice *pulse = (PulseVoice *) hw;
-    int          csLive, csDecr = 0, csSamples, csToWrite, csAvail;
-    size_t       cbAvail, cbToWrite;
+    PulseVoice  *pPulse = (PulseVoice *) hw;
+    int          cFramesLive;
+    int          cFramesWritten = 0;
+    int          csSamples;
+    int          cFramesToWrite;
+    int          cFramesAvail;
+    size_t       cbAvail;
+    size_t       cbToWrite;
     uint8_t     *pu8Dst;
     st_sample_t *psSrc;
 
-    csLive = audio_pcm_hw_get_live_out (hw);
-    if (!csLive)
+    cFramesLive = audio_pcm_hw_get_live_out (hw);
+    if (!cFramesLive)
         return 0;
 
     pa_threaded_mainloop_lock(g_pMainLoop);
 
-    cbAvail = pa_stream_writable_size (pulse->pStream);
+    cbAvail = pa_stream_writable_size (pPulse->pStream);
     if (cbAvail == (size_t)-1)
     {
-        if (pulse->cErrors < MAX_LOG_REL_ERRORS)
+        if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
         {
             int rc = pa_context_errno(g_pContext);
-            pulse->cErrors++;
+            pPulse->cErrors++;
             LogRel(("Pulse: Failed to determine the writable size: %s\n",
                      pa_strerror(rc)));
-            pulse_check_fatal(pulse, rc);
         }
         goto unlock_and_exit;
     }
 
-    csAvail   = cbAvail >> hw->info.shift; /* bytes => samples */
-    csDecr    = audio_MIN (csLive, csAvail);
-    csSamples = csDecr;
+    cFramesAvail   = cbAvail >> hw->info.shift; /* bytes => samples */
+    cFramesWritten = audio_MIN (cFramesLive, cFramesAvail);
+    csSamples      = cFramesWritten;
 
     while (csSamples)
     {
         /* split request at the end of our samples buffer */
-        csToWrite = audio_MIN (csSamples, hw->samples - hw->rpos);
-        cbToWrite = csToWrite << hw->info.shift;
-        psSrc     = hw->mix_buf + hw->rpos;
-        pu8Dst    = advance (pulse->pPCMBuf, hw->rpos << hw->info.shift);
-
-        hw->clip (pu8Dst, psSrc, csToWrite);
-
-        if (pa_stream_write (pulse->pStream, pu8Dst, cbToWrite,
+        cFramesToWrite = audio_MIN (csSamples, hw->samples - hw->rpos);
+        cbToWrite      = cFramesToWrite << hw->info.shift;
+        psSrc          = hw->mix_buf + hw->rpos;
+        pu8Dst         = advance (pPulse->pPCMBuf, hw->rpos << hw->info.shift);
+
+        hw->clip (pu8Dst, psSrc, cFramesToWrite);
+
+        if (pa_stream_write (pPulse->pStream, pu8Dst, cbToWrite,
                              /*cleanup_callback=*/NULL, 0, PA_SEEK_RELATIVE) < 0)
         {
             LogRel(("Pulse: Failed to write %d samples: %s\n",
-                    csToWrite, pa_strerror(pa_context_errno(g_pContext))));
-            break;
-        }
-        hw->rpos   = (hw->rpos + csToWrite) % hw->samples;
-        csSamples -= csToWrite;
+                    cFramesToWrite, pa_strerror(pa_context_errno(g_pContext))));
+            break;
+        }
+        hw->rpos   = (hw->rpos + cFramesToWrite) % hw->samples;
+        csSamples -= cFramesToWrite;
     }
 
@@ -421,5 +414,5 @@
     pa_threaded_mainloop_unlock(g_pMainLoop);
 
-    return csDecr;
+    return cFramesWritten;
 }
 
@@ -431,6 +424,6 @@
 static void stream_success_callback(pa_stream *pStream, int success, void *userdata)
 {
-    PulseVoice *pulse = (PulseVoice *) userdata;
-    pulse->fOpSuccess = success;
+    PulseVoice *pPulse = (PulseVoice *) userdata;
+    pPulse->fOpSuccess = success;
     pa_threaded_mainloop_signal(g_pMainLoop, 0);
 }
@@ -438,17 +431,22 @@
 typedef enum
 {
-    Unpause  = 0,
-    Pause    = 1,
-    Flush    = 2,
-    Trigger  = 3
+    Unpause,
+    Pause,
+    Trigger,
+    Drain
 } pulse_cmd_t;
 
-static int pulse_ctrl (HWVoiceOut *hw, pulse_cmd_t cmd)
-{
-    PulseVoice *pulse = (PulseVoice *) hw;
+static int pulse_ctrl (PulseVoice *pPulse, pulse_cmd_t cmd)
+{
     pa_operation *op = NULL;
-
-    if (!pulse->pStream)
+    const char *cmd_str = cmd == Unpause ? "unpause" :
+                          cmd == Pause   ? "pause"   :
+                          cmd == Trigger ? "trigger" :
+                          cmd == Drain   ? "drain"   : NULL;
+
+    if (!pPulse->pStream)
         return 0;
+
+    LogRel(("Pulse: ctrl cmd=%s\n", cmd_str));
 
     pa_threaded_mainloop_lock(g_pMainLoop);
@@ -456,14 +454,14 @@
     {
         case Pause:
-            op = pa_stream_cork(pulse->pStream, 1, stream_success_callback, pulse);
+            op = pa_stream_cork(pPulse->pStream, 1, stream_success_callback, pPulse);
             break;
         case Unpause:
-            op = pa_stream_cork(pulse->pStream, 0, stream_success_callback, pulse);
-            break;
-        case Flush:
-            op = pa_stream_flush(pulse->pStream, stream_success_callback, pulse);
+            op = pa_stream_cork(pPulse->pStream, 0, stream_success_callback, pPulse);
+            break;
+        case Drain:
+            op = pa_stream_drain(pPulse->pStream, stream_success_callback, pPulse);
             break;
         case Trigger:
-            op = pa_stream_trigger(pulse->pStream, stream_success_callback, pulse);
+            op = pa_stream_trigger(pPulse->pStream, stream_success_callback, pPulse);
             break;
         default:
@@ -472,15 +470,17 @@
     if (!op)
     {
-        if (pulse->cErrors < MAX_LOG_REL_ERRORS)
+        if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
         {
             int rc = pa_context_errno(g_pContext);
-            pulse->cErrors++;
-            LogRel(("Pulse: Failed ctrl cmd=%d to stream: %s\n",
-                    cmd, pa_strerror(pa_context_errno(g_pContext))));
-            pulse_check_fatal(pulse, rc);
+            pPulse->cErrors++;
+            LogRel(("Pulse: Failed ctrl cmd=%s to stream: %s\n", cmd_str, pa_strerror(rc)));
         }
     }
     else
+    {
+        while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
+            pa_threaded_mainloop_wait(g_pMainLoop);
         pa_operation_unref(op);
+    }
 
 unlock_and_exit:
@@ -494,10 +494,13 @@
     {
         case VOICE_ENABLE:
-            pulse_ctrl(hw, Unpause);
-            pulse_ctrl(hw, Trigger);
-            break;
+            pulse_ctrl((PulseVoice *)hw, Unpause);
+            break;
+
         case VOICE_DISABLE:
-            pulse_ctrl(hw, Flush);
-            break;
+            pulse_ctrl((PulseVoice *)hw, Trigger);
+            pulse_ctrl((PulseVoice *)hw, Drain);
+            pulse_ctrl((PulseVoice *)hw, Pause);
+            break;
+
         default:
             return -1;
@@ -508,36 +511,33 @@
 static int pulse_init_in (HWVoiceIn *hw, audsettings_t *as)
 {
-    PulseVoice *pulse = (PulseVoice *) hw;
-    struct pulse_params_req req;
-    struct pulse_params_obt obt;
-    audfmt_e effective_fmt;
-    int endianness;
+    PulseVoice *pPulse = (PulseVoice *) hw;
     audsettings_t obt_as;
 
-    req.pa_format   = aud_to_pulsefmt (as->fmt);
-    req.freq        = as->freq;
-    req.nchannels   = as->nchannels;
-
-    if (pulse_open (/*fIn=*/1, &req, &obt, &pulse->pStream))
+    pPulse->SampleSpec.format   = aud_to_pulsefmt (as->fmt);
+    pPulse->SampleSpec.rate     = as->freq;
+    pPulse->SampleSpec.channels = as->nchannels;
+
+    /* XXX check these values */
+    pPulse->BufAttr.fragsize    = (pa_bytes_per_second(&pPulse->SampleSpec)
+                                  * conf.buffer_msecs_in) / 2000; /* one tenth */
+    pPulse->BufAttr.maxlength   = (pa_bytes_per_second(&pPulse->SampleSpec)
+                                  * conf.buffer_msecs_in) / 1000;
+    /* Other memebers of pa_buffer_attr are ignored for record streams */
+
+    if (pulse_open (1, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
         return -1;
 
-    if (pulse_to_audfmt (obt.pa_format, &effective_fmt, &endianness))
-    {
-        LogRel(("Pulse: Cannot find audio format %d\n", obt.pa_format));
+    if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
+    {
+        LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
         return -1;
     }
 
-    obt_as.freq       = obt.freq;
-    obt_as.nchannels  = obt.nchannels;
-    obt_as.fmt        = effective_fmt;
-    obt_as.endianness = endianness;
-
+    obt_as.freq       = pPulse->SampleSpec.rate;
+    obt_as.nchannels  = pPulse->SampleSpec.channels;
     audio_pcm_init_info (&hw->info, &obt_as);
-
-    /* pcm_in: reserve twice as the maximum buffer length because of peek()/drop(). */
-    hw->samples = 2 * (obt.buffer_size >> hw->info.shift);
-
-    /* no buffer for input */
-    pulse->pPCMBuf = NULL;
+    hw->samples       = audio_MIN(pPulse->BufAttr.fragsize * 10, pPulse->BufAttr.maxlength)
+                          >> hw->info.shift;
+    pPulse->pu8PeekBuf = NULL;
 
     return 0;
@@ -546,15 +546,13 @@
 static void pulse_fini_in (HWVoiceIn *hw)
 {
-    PulseVoice *pulse = (PulseVoice *)hw;
-    if (pulse->pStream)
-    {
-        pa_stream_disconnect(pulse->pStream);
-        pa_stream_unref(pulse->pStream);
-        pulse->pStream = NULL;
-    }
-    if (pulse->pPCMBuf)
-    {
-        RTMemFree (pulse->pPCMBuf);
-        pulse->pPCMBuf = NULL;
+    PulseVoice *pPulse = (PulseVoice *)hw;
+
+    if (pPulse->pStream)
+    {
+        pa_threaded_mainloop_lock(g_pMainLoop);
+        pa_stream_disconnect(pPulse->pStream);
+        pa_stream_unref(pPulse->pStream);
+        pa_threaded_mainloop_unlock(g_pMainLoop);
+        pPulse->pStream = NULL;
     }
 }
@@ -562,48 +560,76 @@
 static int pulse_run_in (HWVoiceIn *hw)
 {
-    PulseVoice *pulse = (PulseVoice *) hw;
-    int    csDead, csDecr = 0, csSamples, csRead, csAvail;
-    size_t cbAvail;
-    const void  *pu8Src;
-    st_sample_t *psDst;
-
-    csDead = hw->samples - audio_pcm_hw_get_live_in (hw);
-
-    if (!csDead)
-        return 0; /* no buffer available */
-
+    PulseVoice *pPulse = (PulseVoice *) hw;
+    const int hwshift = hw->info.shift;
+    int       cFramesRead = 0;    /* total frames which have been read this call */
+    int       cFramesAvail;       /* total frames available from pulse at start of call */
+    int       cFramesToRead;      /* the largest amount we want/can get this call */
+    int       cFramesToPeek;      /* the largest amount we want/can get this peek */
+
+    /* We should only call pa_stream_readable_size() once and trust the first value */
     pa_threaded_mainloop_lock(g_pMainLoop);
-
-    if (pa_stream_peek(pulse->pStream, &pu8Src, &cbAvail) < 0)
-    {
-        LogRel(("Pulse: Peek failed: %s\n",
-                pa_strerror(pa_context_errno(g_pContext))));
-        goto unlock_and_exit;
-    }
-    if (!pu8Src)
-        goto unlock_and_exit;
-
-    csAvail = cbAvail >> hw->info.shift;
-    csDecr  = audio_MIN (csDead, csAvail);
-
-    csSamples = csDecr;
-
-    while (csSamples)
-    {
-        /* split request at the end of our samples buffer */
-        psDst      = hw->conv_buf + hw->wpos;
-        csRead     = audio_MIN (csSamples, hw->samples - hw->wpos);
-        hw->conv (psDst, pu8Src, csRead, &nominal_volume);
-        hw->wpos   = (hw->wpos + csRead) % hw->samples;
-        csSamples -= csRead;
-        pu8Src     = (const void*)((uint8_t*)pu8Src + (csRead << hw->info.shift));
-    }
-
-    pa_stream_drop(pulse->pStream);
-
-unlock_and_exit:
+    cFramesAvail = pa_stream_readable_size(pPulse->pStream) >> hwshift;
     pa_threaded_mainloop_unlock(g_pMainLoop);
 
-    return csDecr;
+    /* If the buffer was not dropped last call, add what remains */
+    if (pPulse->pu8PeekBuf)
+        cFramesAvail += (pPulse->cbPeekBuf - pPulse->offPeekBuf) >> hwshift;
+
+    cFramesToRead = audio_MIN(cFramesAvail, hw->samples - audio_pcm_hw_get_live_in(hw));
+    for (; cFramesToRead; cFramesToRead -= cFramesToPeek)
+    {
+        /* If there is no data, do another peek */
+        if (!pPulse->pu8PeekBuf)
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            pa_stream_peek(pPulse->pStream, (const void**)&pPulse->pu8PeekBuf, &pPulse->cbPeekBuf);
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+            pPulse->offPeekBuf = 0;
+            if (!pPulse->cbPeekBuf)
+                break;
+        }
+
+        cFramesToPeek = audio_MIN((signed)(  pPulse->cbPeekBuf
+                                           - pPulse->offPeekBuf) >> hwshift,
+                                  cFramesToRead);
+
+        /* Check for wrapping around the buffer end */
+        if (hw->wpos + cFramesToPeek > hw->samples)
+        {
+            int cFramesDelta = hw->samples - hw->wpos;
+
+            hw->conv(hw->conv_buf + hw->wpos,
+                     pPulse->pu8PeekBuf + pPulse->offPeekBuf,
+                     cFramesDelta,
+                     &nominal_volume);
+
+            hw->conv(hw->conv_buf,
+                     pPulse->pu8PeekBuf + pPulse->offPeekBuf + (cFramesDelta << hwshift),
+                     cFramesToPeek - cFramesDelta,
+                     &nominal_volume);
+        }
+        else
+        {
+            hw->conv(hw->conv_buf + hw->wpos,
+                     pPulse->pu8PeekBuf + pPulse->offPeekBuf,
+                     cFramesToPeek,
+                     &nominal_volume);
+        }
+
+        cFramesRead += cFramesToPeek;
+        hw->wpos = (hw->wpos + cFramesToPeek) % hw->samples;
+        pPulse->offPeekBuf += cFramesToPeek << hwshift;
+
+        /* If the buffer is done, drop it */
+        if (pPulse->offPeekBuf == pPulse->cbPeekBuf)
+        {
+            pa_threaded_mainloop_lock(g_pMainLoop);
+            pa_stream_drop(pPulse->pStream);
+            pa_threaded_mainloop_unlock(g_pMainLoop);
+            pPulse->pu8PeekBuf = NULL;
+        }
+    }
+
+    return cFramesRead;
 }
 
@@ -615,4 +641,22 @@
 static int pulse_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
+    PulseVoice *pPulse = (PulseVoice *)hw;
+
+    switch (cmd)
+    {
+        case VOICE_ENABLE:
+            pulse_ctrl((PulseVoice *)hw, Unpause);
+            break;
+        case VOICE_DISABLE:
+            if (pPulse->pu8PeekBuf)
+            {
+                pa_stream_drop(pPulse->pStream);
+                pPulse->pu8PeekBuf = NULL;
+            }
+            pulse_ctrl((PulseVoice *)hw, Pause);
+            break;
+        default:
+            return -1;
+    }
     return 0;
 }
@@ -628,4 +672,5 @@
         return NULL;
     }
+
     if (!(g_pMainLoop = pa_threaded_mainloop_new()))
     {
@@ -634,4 +679,5 @@
         goto fail;
     }
+
     if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VBox")))
     {
@@ -640,4 +686,5 @@
         goto fail;
     }
+
     if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
     {
@@ -689,4 +736,5 @@
         g_pContext = NULL;
     }
+
     if (g_pMainLoop)
     {
@@ -694,4 +742,5 @@
         g_pMainLoop = NULL;
     }
+
     return NULL;
 }
@@ -701,4 +750,5 @@
     if (g_pMainLoop)
         pa_threaded_mainloop_stop(g_pMainLoop);
+
     if (g_pContext)
     {
@@ -707,4 +757,5 @@
         g_pContext = NULL;
     }
+
     if (g_pMainLoop)
     {
@@ -712,4 +763,5 @@
         g_pMainLoop = NULL;
     }
+
     (void) opaque;
 }
