QtMoko audio state work

Neil Jerram neil at ossau.homelinux.net
Mon Jan 28 22:51:38 CET 2013

Neil Jerram <neil at ossau.homelinux.net> writes:

> I had an unsuccessful call this evening; it was an incoming one.  The
> gsm-voice-routing log for it is below; I plan to analyse this more
> myself to see if there's a repeating pattern of overruns and underruns,
> but thought it worth posting in case someone else sees and understands
> the pattern first.

Well the pattern is clear enough, and persists in exactly the same form
throughout the whole call.  r_mod (capture from the modem) gets an
overrun every 4 loop iterations.  p_ear and p_mod (playback to the
earpiece and to the modem) get an underrun every 8 iterations.  r_mic
(capture from the microphone) doesn't get any overruns at all.

For context, here's a representation of the gsm-voice-routing main loop.

      +-------+     +-------+     +-------+     +-------+
   .->| Read  |--+->| Read  |--+->| Write |---->| Write |--.
  /   | r_mic |  |  | r_mod |  |  | p_ear |     | p_mod |   \
 /    +-------+  |  +-------+  |  +-------+     +-------+    \
 \         (if overrun)  (if overrun)                        /
  \             /             /                             /

I had two ideas for explaining the observed over/underruns, but neither
of them makes complete sense when we look at the full detail.

#1 was that the modem sound card was running slightly faster than the
main sound card.  (In theory they should both be at 8kHz.)  However I
think that is disproved by the fact that I got _exactly_ the same
underrun pattern for p_ear and p_mod.  I think that means that the rates
of the two sound cards must differ by less than 1 period over the 16s
duration of the call that I logged, i.e. by less than 0.2%.

(The period - i.e. how much audio data is read or written in each block
shown above - is 32ms.)

#2 was that gsm-voice-routing might not be getting enough CPU.  The
period time is 32ms; let's call that P.  Also note that
gsm-voice-routing uses hardware buffers that have room for 4P.

Now suppose that the average time for gsm-voice-routing to iterate round
its main loop is more than P, say 1.9P.  After 4 iterations, the capture
devices have captured 7.6P, but gsm-voice-routing has only read 4P from
them - so they have 3.6P still available and are close to overrunning.

The loop always reads r_mic first, so manages to do that before r_mic
overruns, reducing its remaining data to 2.6P.  But that took a bit of
time, and that was long enough for r_mod to fill from 3.6P to 4P, so
that 'Read r_mod' reports an overrun.

r_mod's buffer is now reset to empty and the main loop does a
'continue', which means that it skips the 'Write's and loops back to
reading r_mic again.

For p_ear and p_mod, the underrun every 8 iterations is roughly
explained by the fact that they have a start threshold of 4 periods.
Hence, after an underrun, it takes some iterations for the playback
buffers to fill up to their start threshold, then 4 and a bit iterations
at 1.9P each for them to be emptied (faster than the loop can keep them
filled) and eventually see another underrun.

But there are several problems with this explanation.

- It doesn't actually explain why we never see r_mic overruns.  After an
  r_mod overrun, r_mod's buffer is reset to 0 but r_mic's buffer is left
  unchanged.  So now r_mic is ahead of r_mod, and we ought to see an
  r_mic overrun before the next r_mod overrun...

- If the loop time was nearly 2P, it should take only 2 and a bit
  iterations for p_ear and p_mod to fill to their start threshold.  Plus
  4 iterations to empty again, and that only makes 6 (and a bit), not 8.

- The log shows 519 loop iterations, and QtMoko recorded the call
  duration as 17s, so the average loop iteration time was 32.8ms,
  i.e. almost exactly P as it should be, and nowhere near 2P.

So the head scratching continues.  Any ideas?


More information about the community mailing list