CamillaDSP - Cross-platform IIR and FIR engine for crossovers, room correction etc

Latency issue introduced with CDSP 3.0, alsa.

I used to set a 250 ms delay to the video with KODI in 3b2 to keep the audio and video in sync. Youtube videos in chromium based browsers were not perfectly in sync, but it was tolerable; movies and tv series watched in chromium based browsers were in sync.

Now with v 3.0, I need to set the video delay in Kodi to 800 ms. Youtube videos are all out of sync, badly. Same with movies and tv series. Sometimes, they might start to play in sync, but latency builds up fast and videos are unwatchable. It's perfectly repeatable with all linux kernel versions I tested. Killing CDSP 3.0 and starting 3b2 restores sync.

Strangely, I have a CDSP config where there is, or should be no processing and it's affected as well, to a lesser degree. It should be transparent, shouldn't it? There it is

Code:
description: FL, FR, RL, RR, FC, LFE
devices:
  adjust_period: 30
  capture:
    channels: 6
    device: hw:Loopback,1,0
    format: S32LE
    type: Alsa
  capture_samplerate: null
  chunksize: 2048
  enable_rate_adjust: true
  playback:
    channels: 6
    device: pipewire
    format: S32LE
    type: Alsa
  queuelimit: 16
  rate_measure_interval: null
  samplerate: 48000
  silence_threshold: -80
  silence_timeout: 3
  stop_on_rate_change: null
  target_level: 6143
  volume_ramp_time: null
filters: null
mixers:
  stereo:
    channels:
      in: 6
      out: 6
    mapping:
    - dest: 0
      mute: null
      sources:
      - channel: 0
        gain: 0
        inverted: false
        mute: null
        scale: dB
    - dest: 1
      mute: null
      sources:
      - channel: 1
        gain: 0
        inverted: false
        mute: null
        scale: dB
    - dest: 4
      mute: null
      sources:
      - channel: 4
        gain: 0
        inverted: false
        mute: null
        scale: dB
    - dest: 5
      mute: null
      sources:
      - channel: 5
        gain: 0
        inverted: false
        mute: null
        scale: dB
    - dest: 2
      mute: null
      sources:
      - channel: 2
        gain: 0
        inverted: false
        mute: null
        scale: dB
    - dest: 3
      mute: null
      sources:
      - channel: 3
        gain: 0
        inverted: false
        mute: null
        scale: dB
pipeline:
- bypassed: false
  description: null
  name: stereo
  type: Mixer
processors: null
title: 6 channels flat - no filters
 
I've seven the word "recursive" a few times here now... we cant send data "backwards" in a pipe, can we - so its about repetition instead?
Section 4 of the paper by Ralph Glasgal, 360° Localization via 4.x RACE Processing by Ralph Glasgal explains the recursive nature of the process well I find. The paper can be found fairly easily on the web. To achieve this in dsp you can "by repetition", as you mention, add delayed, inverted and attenuated copies of the original signal to the appropriate channel.
 
Latency issue introduced with CDSP 3.0, alsa.
Please post the logs from the first minute or so after starting cdsp, with debug log level (-v).
I've seven the word "recursive" a few times here now... we cant send data "backwards" in a pipe, can we - so its about repetition instead?

//
It should be done with recursion, but can be approximately with repetition. That can be done with the existing building blocks today, and next version adds a new processor that does the recursion bit.
 
  • Like
Reactions: TNT
@HenrikEnquist : IIUC the only applicable difference between CDSP 3.0 and the previous beta 2 is https://github.com/HEnquist/camilladsp/commit/edbb4ce1b41701322ebe0f534cb2558111d180c5 . Please I do not understand https://github.com/HEnquist/camilla...e43bb729daea8df2988266a047a0a4f680caR242-R245 vs. https://github.com/HEnquist/camilla...e43bb729daea8df2988266a047a0a4f680caR187-R190 The current_delay method for PlaybackBufferManager is defined (sort of) according to the alsa delay vs. avail definition https://stackoverflow.com/a/41278817/15717902 , but why the delay for capture is inverse to that (i.e. equal to avail)? There definitely was a lot of thinking behind that (IMO rather criticial) commit, please would it be possible to put in some explaining comments? Thanks for considering.
 
The capture delay method isn't used anywhere, I just implemented it because it's required by the trait. For playback, the delay is basically, if I write a single frame now, how long will it take before it is heard. So for capture I did something reasonably equivalent: if I read a single frame now, how long ago was that frame captured? But since it's not used, it could just as well be a panic!().

The reason for replacing snd_pcm_delay() was that it causes glitches. Not very often, but still. I haven't investigated exactly why, but calling it repeatedly inside the playback loops wasn't a good idea.
 
  • Like
Reactions: mdsimon2
The capture delay method isn't used anywhere, I just implemented it because it's required by the trait. For playback, the delay is basically, if I write a single frame now, how long will it take before it is heard. So for capture I did something reasonably equivalent: if I read a single frame now, how long ago was that frame captured?

I understand it's not used, still IMO the current implemention is a bit hard to understand. Would the "how long ago was that frame captured" be represented by amount of samples already collected in the buffer, as written by the soundcard (i.e. the delay), instead of the amount of remaining free space available (avail)?
But since it's not used, it could just as well be a panic!().
Sounds like a good solution to not-implemented 🙂

The reason for replacing snd_pcm_delay() was that it causes glitches. Not very often, but still. I haven't investigated exactly why, but calling it repeatedly inside the playback loops wasn't a good idea.
IIUC snd_pcm_delay can call snd_pcm_dma_buffer_sync https://elixir.bootlin.com/linux/v6.12.6/source/sound/core/pcm_native.c#L3023-L3036 , whereas snd_pcm_avail is just simple arithmetics , e.g. https://elixir.bootlin.com/linux/v6.12.6/source/include/sound/pcm.h#L816-L832 However, as a result, snd_pcm_avail may provide less up-to-date information than snd_pcm_delay. Maybe that is why @QuickDraw McGraw hits the inconsistent latency?
 
Well then there are no logs related to the alsa playback 🙂 The log needs to capture the start of the actual playback which defines the initial latency. Best to start CDSP from command line, not as a service controlled by its GUI. It will start playback right away, not waiting for the play websockets command from the GUI
 
I spent over an hour trying to figure out why my old configs were failing to load.
Please could you update the section of the Readme covering the pipeline to make the change to the channels parameter a bit clearer.
As an example, CDSP 2 was perfectly happy with a pipeline that specified
Code:
  channel: 0
Simply updating this to
Code:
  channels: 0
will fail, with the error produced (via --check) pointing unhelpfully to the first line of the pipeline section, rather than the line actually generating the error.
Instead you need to enter
Code:
  channels: [0]
or alternatively
Code:
  channels:
  - 0
The Readme does show an example in which the square brackets are used to affect mutiple channels (i.e channels: [0,1]), But the square brackets are not optional, and even a single channel needs to be enclosed in them.
I assume this is a side effect of the parser you're using.
 
Yeah, it's pretty buried.
Ideally, it would be nice to compile a list of all the changes that need to be made to v2 configs to get them compatible with v3. This would make things a lot easier for those upgrading.

So far I have:
File capture and playback
The old method of using
Code:
  type: File
  filename: /some/file.here
needs to be changed depending on the filetype, either
Code:
  type: RawFile
  filename: /some/file.raw
or
Code:
  type: WavFile
  filename: /some/file.wav
or, for Unix systems piping through stdin and stdout, just
Code:
  type: Stdin
  ...
  type: Stdout
with no filename required

Channels in the pipeline
These now all need to be specified as json lists, even when dealing with a single channel, i.e.
Code:
  channels: [0]
  ...
  channels: [0,1]
  ...
  channels: [0,2,4]
  ...
  channels: null
with the last 'null' example indicating that all channels in the pipeline at that point will be affected.
 
@QuickDraw McGraw : Thanks.

Since your adjust_period is 30 seconds, one minute of capture contains only 2 rate-adjust action. Any reason why this long adjustment time? If you need low latency, small chunksize and frequent rate adjustments (like a few secs) are better.

Nevertheless - you have pipewire in the chain. Do you know what causes the large latency, CDSP or PW? I would guess CDSP does not cause more than some 200ms of latency (2048 frames of capture chunksize + some processing time + 6143 frames of the target level for 48kHz samplerate)

The debug-level log does not show any issue, trace level (-vv) would tell more. I would suggest to lower the adjust_period to e.g. 5 seconds and log at trace level so that every write/delay/avail operation is listed. The moment of the very first write defines the overall latency of CDSP, because then the playback stream is clocked by the output device (pipewire -> output soundcard). The trace log will show how precisely the timing works in your case. But it will definitely not be a difference of hundreds of ms as you are complaining, absolutely not. How did you measure the overall latency?

Please when making the zip, zip directly the file, so that it's not 4 directories deep the zip - lots of clicking to unzip 🙂
 
Since your adjust_period is 30 seconds, one minute of capture contains only 2 rate-adjust action. Any reason why this long adjustment time? If you need low latency, small chunksize and frequent rate adjustments (like a few secs) are better.
Looks like you've spotted the problem immediately. I blindly copied the setting from a sample config file or several. It worked util v 3.0.
Setting the adjust period to 5 eliminated the problem. I tested for 15 minutes and I'm getting identical latency as 3b2 or 2.3.
Nevertheless - you have pipewire in the chain. Do you know what causes the large latency, CDSP or PW?
No idea. No latency when CDSP is not running though.
I would guess CDSP does not cause more than some 200ms of latency (2048 frames of capture chunksize + some processing time + 6143 frames of the target level for 48kHz samplerate)
250 would be more what I've been experimenting in Kodi. IIRC it's not noticeable in youtube videos.
The debug-level log does not show any issue, trace level (-vv) would tell more. I would suggest to lower the adjust_period to e.g. 5 seconds and log at trace level so that every write/delay/avail operation is listed. The moment of the very first write defines the overall latency of CDSP, because then the playback stream is clocked by the output device (pipewire -> output soundcard). The trace log will show how precisely the timing works in your case. But it will definitely not be a difference of hundreds of ms as you are complaining, absolutely not.
Started with /usr/local/bin/camilladsp3.300 -l trace -o /home/shizuma/camilladsp.log /home/shizuma/.config/.camilladsp/v3/90deg-D1-test.yml
How did you measure the overall latency?
Not measuring it per se, adjusting it in Kodi with the audio/video sync slider until they are in sync.
Capture d’écran du 2025-01-16 12-58-11.png
Once it's proven strable, I fix it in the config file so it's applied automatically:

Code:
<advancedsettings>
    <video>
        <subsdelayrange>120</subsdelayrange>  <!-- Plage de décalage pour les sous-titres, en secondes. -->
        <audiodelayrange>1</audiodelayrange>  <!-- Plage de décalage pour la synchronisation audio/vidéo, en secondes. -->
        <latency>
             <delay>-250</delay>
        </latency>
      </video>
</advancedsettings>

UPDATE

T
alked too soon. After 30 minutes testing, latency has increased and keeps increasing while the video is still playing. Currently at 600ms. Out of sync again.
Stopped the video and started another one and still out of sync. It builds slowly up to a maximum and stays there around 800 ms.

Capture d’écran du 2025-01-16 13-19-56.png
 

Attachments

Last edited: