Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ALSA: for discussing - avoiding impact of different processing delays on target_level. #282

Open
pavhofman opened this issue May 19, 2023 · 4 comments

Comments

@pavhofman
Copy link
Contributor

pavhofman commented May 19, 2023

Delay measured in the Playback thread and used for averaging is heavily dependent on time the particular chunk spends in processing. This delay is influenced by many factors outside of control of CDSP - momentary CPU speed, momentary OS load, etc.

The current solution is trying to keep the delay (on average) close to some fixed pre-configured target_level. That means that if the processing speed decreases for some reason (different OS load, moving the thread to a core of different processing capability), the feedback tries to adjust the capture rate. Eventhough there was no real change in ratio between capture/playback rates.

If the playback delay entering the averaging was adjusted for the chunk processing time variation, the resultant delay would not be affected by the external factors.

An example implementation (no tests done, just a proposal for discussion) is in pavhofman@352b05a . The patch ignores configured target_level, and uses initial target_level from the first (base) chunk (ideally skipping the initial chunks as in #281 ). For all subsequent chunks the delay is adjusted for processing time difference between the given chunk and the base chunk, thus eliminating the difference in chunk processing times.

@HEnquist
Copy link
Owner

In the current implementation I'm not thinking of the target_level as a desired delay, but instead as a desired safety margin against underruns. So it aims to keep the margin constant. And that means the delay may vary a bit. Would it be better to keep the delay constant and let the margin vary? Probably depends on who you ask! Personally I think I prefer the constant safety margin, but I can probably be persuaded to make it configurable :)

@pavhofman
Copy link
Contributor Author

pavhofman commented May 22, 2023

This is a complex issue. IMO all facts discussed in these github issues (no real issues = problems) here are interrelated.

Chunk 1 arriving to Playback starts the output device and basically defines the initial latency caused by Processing. This latency has no effect on the delay as used in Playback (let's keep calling it margin, for clarity - thanks for the nice name). Even if the initial latency were several seconds.

From that moment on the output device is running, continuously, at constant output clock.

When chunk 2 arrives to the running Playback, the first margin value (device.get_delay() ) is obtained and goes to averaging.

Now there are (at least) two effects which influence the margin:

  1. The capture/playback clock ratio. Faster capture increases the margin systematically.

  2. The Processing latency of chunk X minus the initial latency of chunk 1. If processing takes more than for chunk 1, the margin gets smaller.

The Processing latency is a random distribution. If its average is constant, the diffs get averaged out by the margin averager and the feedback rate corresponds only to the capture/playback ratio.

If the Processing latency average changes (e.g. due to long-term change in CPU load, or switch of CPU frequency etc.), it starts affecting the averaged margin too.

In the current feedback implementation the margin change caused by the average latency change in Processing is slowly "erased" by temporary increase/decrease of the feedback rate. Once the margin returns to the requested level, the feedback rate returns to the capture/playback ratio, until next systematic change in Processing latency

IMO from this POW the current implementation seems correct as it takes care of both the capture/playback ratio and the other systematic effects, keeping the output margin at safe level.

As for the overall latency IN/OUT.

IMO for internal async resampling the overall latency is fixed (determined by the delays in Capture + Playback + initial latency in Processing), as both IN and OUT fixed clocks keep running, regardless any latency in processing.

For rate adjust the overall latency is Capture margin + current Processing latency + Playback margin. If Processing latency changes, the Playback margin changes in opposite direction. But due to the rate feedback the Playback margin gradually returns to the required level. Therefore the settled-down overall latency will be different from before the Processing latency change.

Maybe it would be useful to monitor time progress of the Processing latency, to be able to observe its effect on the margin. Perhaps it would help with troubleshooting xruns as the feedback may not be able to compensate larger changes in Processing latency. This is related to charting the timing situation in the Playback I've proposed earlier. I will get to some crude implementation eventually.

Please correct me if you see anything wrong, there may be many. Thanks!

@HEnquist
Copy link
Owner

Maybe it would be useful to monitor time progress of the Processing latency, to be able to observe its effect on the margin.

This is actually quite easily doable now in next20. There is a new command GetProcessingLoad: https://github.com/HEnquist/camilladsp/blob/next20/websocket.md?plain=1#L113
It returns the time it took to process the last chunk, relative to the time per chunk (defined as chunksize/samplerate)

@t123yh
Copy link
Contributor

t123yh commented Jun 26, 2023

Currently I use 3 techniques to reduce variations on processing time and buffer level:

  • Use cgroup cpuset feature to put camilladsp on a dedicated CPU core
  • Select the "performance" CPU governor to make CPU run at highest speed (or you can reduce max frequency to achieve some power-saving. The whole point is to make CPU frequency stable)
  • Disable cpuidle of the CPU core processing interrupts (usually core0) to avoid interrupt jitter

I get very stable buffer level with these measures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants