<html>
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">On 09.04.2016 03:29, Raymond Yau wrote:<br>
    </div>
    <blockquote
cite="mid:CAN8cciZs2dqe-J=D0vJ3LG-xAkeeGnV8GA0KQL0Hx51vuo9tQQ@mail.gmail.com"
      type="cite">
      <p><br>
        2016-4-9 上午2:23於 "Georg Chini" <<a moz-do-not-send="true"
          href="mailto:georg@chini.tk">georg@chini.tk</a>>寫道:<br>
        ><br>
        > On 08.04.2016 18:01, Tanu Kaskinen wrote:<br>
        ><br>
        >>>>>><br>
        >>>>>> I can't follow that line of reasoning.
        In the beginning the ring buffer<br>
        >>>>>> is filled to max, and once you call
        snd_pcm_start(), data starts to<br>
        >>>>>> move from the ring buffer to other
        buffers (I'll call the other buffers<br>
        >>>>>> the "not-ring-buffer"). Apparently the
        driver "sees" the not-ring-<br>
        >>>>>> buffer only partially, since it reports
        a larger latency than just the<br>
        >>>>>> ring buffer fill level, but it still
        doesn't report the full latency.<br>
        >>>>>> The time between snd_pcm_start() and
        the point where the reported delay<br>
        >>>>>> does not any more equal the written
        amount tells the size of the<br>
        >>>>>> visible part of the not-ring-buffer -
        it's the time it took for the<br>
        >>>>>> first sample to travel from the ring
        buffer to the invisible part of<br>
        >>>>>> the not-ring-buffer. I don't understand
        how the time could say anything<br>
        >>>>>> about the size of the invisible part of
        the not-ring-buffer. Your logic<br>
        >>>>>> "works" only if the visible and
        invisible parts happen to be of the<br>
        >>>>>> same size.<br>
        >>>>>><br>
        >>>>>> You should get the same results by
        calculating<br>
        >>>>>><br>
        >>>>>>      adjusted delay = ring buffer fill
        level + 2 * (reported delay - ring buffer fill level)<br>
        >>>>>><br>
        >>>>>> That formula doesn't make sense, but
        that's how I understand your logic<br>
        >>>>>> works, with the difference that your
        fix is based on one measurement<br>
        >>>>>> only, so it's constant over time, while
        my formula recalculates the<br>
        >>>>>> adjustment every time the delay is
        queried, so the adjustment size<br>
        >>>>>> varies somewhat depending on the
        granularity at which audio moves to<br>
        >>>>>> and from the visible part of the
        not-ring-buffer.<br>
        >>>>>><br>
        >>>>>> In any case, even if your logic
        actually makes sense and I'm just<br>
        >>>>>> misunderstanding something, I don't see
        why the correction should be<br>
        >>>>>> done in pulseaudio instead of the alsa
        driver.<br>
        >>>>><br>
        >>>>>   Well, now I don't understand what you
        mean. The logic is very simple:<br>
        >>>>> If there is a not reported delay between
        the time snd_pcm_start() is<br>
        >>>>> called and the time when the first sample
        is delivered to the DAC, then<br>
        >>>>> this delay will persist and become part of
        the continuous latency.<br>
        >>>>> That's all, what causes the delay is
        completely irrelevant.<br>
        >>>><br>
        >>>>   The code can't know when the first sample
        hits the DAC. The delay<br>
        >>>> reported by alsa is supposed to tell that, but
        if the reported delay is<br>
        >>>> wrong, I don't think you have any way to know
        the real delay.<br>
        >>><br>
        >>>   Yes, the code can know when the first sample hits
        the DAC. I explained it<br>
        >>> already. Before the first sample hits the DAC, the
        delay is growing and<br>
        >>> larger or equal than the number of samples you have
        written to the<br>
        >>> buffer.<br>
        >>> At the moment the delay is smaller than the write
        count, you can be<br>
        >>> sure that at least some audio has been delivered.
        Since the delay is<br>
        >>> decreased by the amount of audio that has been
        delivered to the DAC,<br>
        >>> you can work back in time to the moment when the
        first sample has been<br>
        >>> played.<br>
        >><br>
        >> Yes, you explained that already, but you didn't give a
        convincing<br>
        >> explanation of why the point in time when the delay
        stops growing would<br>
        >> indicate the point when the first sample hit the DAC.<br>
        ><br>
        ><br>
        > See below. The precondition for my thoughts naturally is
        that no<br>
        > samples vanish from the latency reports, maybe that is
        where<br>
        > we are thinking differently.<br>
        ><br>
        ><br>
        >><br>
        >>>>> Maybe what I said above was not complete.
        At the point in time when<br>
        >>>>> the first audio is played, there are two
        delays: First the one that is<br>
        >>>>> reported<br>
        >>>>> by alsa and the other is the difference
        between the time stamps minus<br>
        >>>>> the played audio. If these two delays don't
        match, then there is an<br>
        >>>>> "extra delay" that has to be taken into
        account.<br>
        >>>><br>
        >>>>   The difference between the time stamps is not
        related to how big the<br>
        >>>> invisible part of the buffer is. I'll try to
        illustrate:<br>
        >>>><br>
        >>>> In the beginning, pulseaudio has written 10 ms
        of audio to the ring<br>
        >>>> buffer, and snd_pcm_start() hasn't been called:<br>
        >>>><br>
        >>>> DAC <- ssssssssss|sss|dddddddddd <-
        pulseaudio<br>
        >>>><br>
        >>>> Here "ssssssssss|sss|ddddddddd" is the whole
        buffer between the DAC and<br>
        >>>> pulseaudio. It's divided into three parts; the
        pipe characters separate<br>
        >>>> the different parts. Each letter represents 1
        ms of data. "s" stands<br>
        >>>> for silence and "d" stands for data. The first
        part of the buffer is<br>
        >>>> the invisible part that is not included in the
        delay reports. I've put<br>
        >>>> 10 ms of data there, but it's unknown to the
        driver how big the<br>
        >>>> invisible part is. The middle part of the
        buffer is the "send buffer"<br>
        >>>> that the driver maintains, its size is 3 ms in
        this example. It's<br>
        >>>> filled with silence in the beginning. The third
        part is the ring<br>
        >>>> buffer, containing 10 ms of data from
        pulseaudio.<br>
        >>>><br>
        >>>> At this point the driver reports 10 ms latency.
        It knows it has 3 ms of<br>
        >>>> silence buffered too, which it should include
        in its latency report,<br>
        >>>> but it's stupid, so it only reports the data in
        the ring buffer. The<br>
        >>>> driver has no idea how big the invisible part
        is, so it doesn't include<br>
        >>>> it in the report.<br>
        >>>><br>
        >>>> Now pulseaudio calls snd_pcm_start(), which
        causes data to start moving<br>
        >>>> from the ring buffer to the send buffer. After
        1 ms the situation looks<br>
        >>>> like this:<br>
        >>>><br>
        >>>> DAC <- ssssssssss|ssd|ddddddddd  <-
        pulseaudio<br>
        >>>><br>
        >>>> There's 2 ms of silence in the send buffer and
        1 ms of data. The driver<br>
        >>>> again ignores the silence in the send buffer,
        and reports that the<br>
        >>>> delay is 10 ms, which consists of 1 ms of data
        in the send buffer and 9<br>
        >>>> ms of data in the ring buffer.<br>
        >>>><br>
        >>>> After 2 ms:<br>
        >>>><br>
        >>>> DAC <- ssssssssss|sdd|dddddddd   <-
        pulseaudio<br>
        >>>><br>
        >>>> Reported delay: 10 ms<br>
        >>>><br>
        >>>> After 3 ms:<br>
        >>>><br>
        >>>> DAC <- ssssssssss|ddd|ddddddd    <-
        pulseaudio<br>
        >>>><br>
        >>>> Reported delay: 10 ms<br>
        >>>><br>
        >>>> Let's say pulseaudio refills the ring buffer
        now.<br>
        >>>><br>
        >>>> DAC <- ssssssssss|ddd|dddddddddd <-
        pulseaudio<br>
        >>>><br>
        >>>> Reported delay: 13 ms<br>
        >>>><br>
        >>>> After 4 ms:<br>
        >>>><br>
        >>>> DAC <- sssssssssd|ddd|ddddddddd  <-
        pulseaudio<br>
        >>>><br>
        >>>> The first data chunk has now entered the
        invisible part of the buffer,<br>
        >>>> but it will still take 9 ms before it hits the
        DAC. At this point<br>
        >>>> pulseaudio has written 13 ms of audio, and the
        reported delay is 12 ms.<br>
        >>>> According to your logic, the adjusted delay is
        12 + (4 - 1) = 15 ms,<br>
        >>>> while in reality the latency is 22 ms.<br>
        >>><br>
        >>>   At this point, no audio has been played yet. You
        still have silence in the<br>
        >>> buffer, so alsa would not report back, that samples
        have been played.<br>
        >><br>
        >> But the reported delay stopped growing! That's the
        point where you<br>
        >> claim the first sample hits the DAC, but as my example
        illustrates,<br>
        >> that doesn't seem to be true.<br>
        ><br>
        ><br>
        > In your example it is not true, that's right. But for the
        USB devices it is.<br>
        > They only start decreasing the delay when real audio has
        been played,<br>
        > and they would increase the delay when you write to the
        buffer,<br>
        > I have checked that in the code.<br>
        > And I think any driver that makes samples vanish is so
        severely screwed,<br>
        > that we can't do anything about it. If the driver reports
        complete moonshine<br>
        > numbers, you can't fix it, I agree with you in that
        respect.<br>
        ><br>
        > But that is not the case with USB. There is only some
        missing latency<br>
        > that is not reported - call it transport delay or whatever
        and I suspect a<br>
        > similar delay can be found in other alsa drivers. There is
        no need to figure<br>
        > out the reason for it, it just takes some time after
        snd_pcm_start() was<br>
        > called until the first sample is played - without making
        samples vanish.<br>
        > And in that case the delay can be detected and used by the
        code.<br>
        ><br>
        ><br>
        >><br>
        >>> I choose the point where the first d hits the DAC
        and that is reported<br>
        >>> back by alsa. (see above) I've tried put it all
        together in a document.<br>
        >>> I hope I can finish the part that deals with the
        smoother code today.<br>
        >>> If so, I will send it to you privately because the
        part about<br>
        >>> module-loopback<br>
        >>> is still missing.<br>
        >>> Anyway, even if you think it is wrong I am still
        measuring the correct<br>
        >>> end-to-end latency with my code, so something I am
        doing must be<br>
        >>> right ...<br>
        >><br>
        >> >From what I can tell, that's a coincidence.<br>
        ><br>
        ><br>
        > No, it definitely isn't. If you accept the precondition,
        that samples<br>
        > not simply vanish from the latency reports, it's physics.<br>
        > I would tend to agree that I have overlooked something, if
        the "extra<br>
        > delay" would be the same every time and if I could not
        write down<br>
        > the math for it.<br>
        > But it isn't completely constant (just in the same range)
        and I can<br>
        > write down the math and it matches my measurements. So I am<br>
        > fairly sure that I am right. Did you have a look at my
        document?<br>
        ><br>
        ><br>
        >><br>
        >>>> I don't know how well this model reflects the
        reality of how the usb<br>
        >>>> audio driver works, but this model seems like a
        plausible explanation<br>
        >>>> for why the driver reports delays equalling the
        amount of written data<br>
        >>>> in the beginning, and why the real latency is
        higher than the reported<br>
        >>>> latency at later times.<br>
        >>>><br>
        >>>> I hope this also clarifies why I don't buy your
        argument that the time<br>
        >>>> stamp difference is somehow related to the
        unreported latency.<br>
        >>><br>
        >>>   No, in fact it doesn't.<br>
        >>><br>
        >>>>> Trying to fix up that delay on every
        iteration does not make any sense<br>
        >>>>> at all, it is there from the start and it
        is constant.<br>
        >>>><br>
        >>>>   Commenting on "it is constant": The playback
        latency is the sum of data<br>
        >>>> in various buffers. The DAC consumes one sample
        at a time from the very<br>
        >>>> last buffer, but I presume that all other
        places move data in bigger<br>
        >>>> chunks than one sample. The unreported delay
        can only be constant if<br>
        >>>> data moves to the invisible part of the
        buffering in one sample chunks.<br>
        >>>> Otherwise the latency goes down every time the
        DAC reads a sample, and<br>
        >>>> then when the buffer is refilled at the other
        end, the latency jumps up<br>
        >>>> by the refill amount.<br>
        >>><br>
        >>>   I only said the "extra latency" is constant, not
        the latency as such.<br>
        >>> See your own example above that your argument is
        wrong. Even<br>
        >>> if the audio is moved in chunks through your
        invisible buffer part,<br>
        >>> that part still has the same length all the time.
        When one "d" is<br>
        >>> moved forward another one will replace it.<br>
        >><br>
        >> No, the invisible part is not constant, even though my
        presentation<br>
        >> didn't show the variance. The DAC consumes data from
        the invisible<br>
        >> buffer one sample at a time, and each time it does
        that, the extra<br>
        >> latency decreases by one sample. Data moves from the
        visible part of<br>
        >> the buffer to the invisible part in bigger chunks. I
        didn't specify the<br>
        >> chunk size, but if we assume 1 ms chunks, the extra
        latency grows by 1<br>
        >> ms every time a chunk is transferred from the visible
        part to the<br>
        >> invisible part.<br>
        ><br>
        ><br>
        > Then take any part of the buffer but the last or the first
        bit. All the<br>
        > chunks are always full, so it's constant. The moving bit is
        dealt with<br>
        > elsewhere, (in the smoother) but there is a lot of buffer
        that is always<br>
        > full.<br>
        > And when you take USB, the driver sees only chunks. The
        sample<br>
        > by sample consuming of the DAC is never seen by the driver,
        it gets<br>
        > the notification from USB that a chunk has been played.<br>
        > I'm not sure how it is with HDA, but probably similar.<br>
        ><br>
        >><br>
        >>>>> This is not a negative delay reported by
        alsa, but my "extra latency"<br>
        >>>>> is getting negative, which means playback
        must have started<br>
        >>>>> before snd_pcm_start().<br>
        >>>>> According to Raymond Yau playback seems in
        fact to be started<br>
        >>>>> before snd_pcm_start() for HDA devices, at
        least if I read his last<br>
        >>>>> mail on that topic right. Then the negative
        delays would even make<br>
        >>>>> sense, since data is written to the buffer
        before snd_pcm_start().<br>
        >>>><br>
        >>>>   I had a look at the code to verify the claim
        that we configure alsa to<br>
        >>>> start playback already before we call
        snd_pcm_start(). If we really do<br>
        >>>> that intentionally, then it doesn't make sense
        to call snd_pcm_start()<br>
        >>>> explicitly.<br>
        >>>><br>
        >>>> This is what we do:<br>
        >>>> snd_pcm_sw_params_set_start_threshold(pcm,
        swparams, (snd_pcm_uframes_t) -1)<br>
        >>>><br>
        >>>> Note the casting of -1 to an unsigned integer.
        It seems that the<br>
        >>>> intention is to set as high threshold as
        possible to avoid automatic<br>
        >>>> starting. However, alsa-lib casts the threshold
        back to a signed value<br>
        >>>> when it's used, and I believe the end result is
        indeed that playback<br>
        >>>> starts immediately after the first write. I
        don't know if that matters,<br>
        >>>> since we do the manual snd_pcm_start() call
        immediately after the first<br>
        >>>> write anyway, but it seems like a bug in any
        case.<br>
        >><br>
        >> Not very important, but I'll clarify one thing: I had
        another look, and<br>
        >> I'm not any more sure that the code where I saw the
        casting back to a<br>
        >> signed integer is actually used by pulseaudio. The
        function<br>
        >> is snd_pcm_write_areas(), but pulseaudio doesn't call
        that at least<br>
        >> directly, and I did some searching in alsa-lib too, and
        I didn't find a<br>
        >> call path that would cause snd_pcm_write_areas() to be
        used by<br>
        >> pulseaudio. Even if snd_pcm_write_areas() isn't used,
        though, it's<br>
        >> entirely possible that there's some other code that
        does a similar<br>
        >> cast. I don't know the code is that triggers the
        snd_pcm_start() call<br>
        >> when the ring buffer fill level exceeds the configured
        threshold. It<br>
        >> might be in the kernel.<br>
        >><br>
        >>> OK, this it why I measure an "extra latency" of -60
        to -20 usec.<br>
        >>> So again, if I can measure it and even detect a bug
        that way,<br>
        >>> don't you think there must be some truth in what
        I'm saying?<br>
        >><br>
        >> Do I understand correctly that your "extra latency" is
        affected by<br>
        >> whether snd_pcm_start() is called implicitly in
        mmap_write() or<br>
        >> explicitly after mmap_write()? The time when
        mmap_write() is called<br>
        >> doesn't affect the latency in the long term.<br>
        ><br>
        > It does. It isn't much, but if playback starts earlier, the
        delay<br>
        > will be exactly that amount less even after 10 hours of
        playback.<br>
        > Let's assume you have 10ms of audio to write to the buffer.<br>
        > During the time, when you write, samples are coming in.<br>
        > Let's say it takes 100 usec to write the buffer. If you
        start<br>
        > playback after the write, this will be 100 usec additional
        delay.<br>
        > 5 samples have accumulated.<br>
        > If you start playback immediately after the first bit of
        data is<br>
        > written this might take much less time, say 20 usec.<br>
        > So your delay is four samples less and it will remain that
        way<br>
        > until the sink is stopped. There is nothing that would take
        away<br>
        > the delay.<br>
        ><br>
        ><br>
        >> The smoother will produce<br>
        >> wrong values if it's not started at the same time as
        snd_pcm_start() is<br>
        >> called, but I presume the smoother is able to fix such
        inaccuracies<br>
        >> over time, so it doesn't matter that much when the
        snd_pcm_start() is<br>
        >> called. So isn't it a bad thing if your "extra latency"
        permanently<br>
        >> includes something that doesn't have any real effect
        after some time?<br>
        ><br>
        ><br>
        > Yes, it is affected by it and it should be, because the
        "extra delay"<br>
        > is the time between snd_pcm_start() and the first sample
        being<br>
        > played. So if the first samples are played before
        snd_pcm_start()<br>
        > the "extra latency" will become negative. And as explained
        above,<br>
        > it has permanent effect. Somehow you seem to be of the
        opinion<br>
        > that all delays that are not controlled by the pulseaudio
        code<br>
        > vanish magically, but they don't.<br>
        ><br>
        > For the reported latency, it just means, that it will
        become slightly<br>
        > smaller. As I said, the smoother does not use the "extra
        delay"<br>
        > for anything, it is only calculated once when the origin
        for the<br>
        > smoother is set and added later as an offset, when
        get_latency()<br>
        > is called.<br>
        ></p>
      <p>as your log had two "Starting Playback" message, can you call
        snd_pcm_dump after snd_pcm_start to find value of  appl_ptr,</p>
    </blockquote>
    I will, but there is a suspend message between the two "Starting
    Playback" messages:<br>
    <br>
     sink.c: Suspending sink
    alsa_output.usb-0d8c_C-Media_USB_Headphone_Set-00.analog-stereo due
    to changing the sample rate.<br>
     sink.c: Suspend cause of sink
    alsa_output.usb-0d8c_C-Media_USB_Headphone_Set-00.analog-stereo is
    0x0020, suspending<br>
    <br>
    So I don't think there is a problem, but I will do your test and let
    you know the results.<br>
    <br>
    <blockquote
cite="mid:CAN8cciZs2dqe-J=D0vJ3LG-xAkeeGnV8GA0KQL0Hx51vuo9tQQ@mail.gmail.com"
      type="cite">
      <p>  do pulseaudio prebuf mean minimum first write ?</p>
    </blockquote>
    <br>
    Don't know, according to Tanu, the first write will fill the buffer
    to the<br>
    configured latency. The log also shows this. Because the buffer of<br>
    module-loopback is filled when playback is started, buffering should<br>
    not be a problem.<br>
    <br>
    <blockquote
cite="mid:CAN8cciZs2dqe-J=D0vJ3LG-xAkeeGnV8GA0KQL0Hx51vuo9tQQ@mail.gmail.com"
      type="cite">
      <p>Do loopback module stop the running pcm stream ? <br>
      </p>
      <p>Seem pulseaudio does not use snd_pcm_drop nor snd_pcm_drain, 
        how can the running pcm stream stop? <br>
      </p>
    </blockquote>
    This is the beginning of the suspend function of module-loopback, so
    obviously<br>
    snd_pcm_close close is called instead of snd_pcm_drop or _drain (I
    did not<br>
    change anything here):<br>
    <br>
    static int suspend(struct userdata *u) {<br>
        pa_assert(u);<br>
        pa_assert(u->pcm_handle);<br>
    <br>
        /* Let's suspend -- we don't call snd_pcm_drain() here since
    that might<br>
         * take awfully long with our long buffer sizes today. */<br>
        snd_pcm_close(u->pcm_handle);<br>
    <br>
    <br>
  </body>
</html>