bastibe / SoundCard

A Pure-Python Real-Time Audio Library
https://soundcard.readthedocs.io
BSD 3-Clause "New" or "Revised" License
689 stars 70 forks source link

PA_STREAM_ADJUST_LATENCY and `_block_size` in `_Recorder` #17

Closed mpariente closed 6 years ago

mpariente commented 6 years ago

I was trying to understand Latency Control and reading the docs of PulseAudio. I found that when the _pa.PA_STREAM_ADJUST_LATENCY flag is set (almost everywhere in Soundcard), we have to specify the latency we want in the buffer_atr.

Since the changes in the _Recorder, we can specify blocksize which will be used to compute bufattr.fragsize, but won't be the latency we require in case we also specify numframes in the record method.

If you recall, we talked about the value of blocksize and numframes during the PR.

Another question, what should be the value of blocksize for realtime recording/playback loop? Should it be similar to numframes or much smaller?

I was thinking about this, too. It should probably be much smaller than your expected numframes. We'll have to experiment with the finished code to find a reasonable value.

Is this a problem? What could it cause?

I'm researching on this because I try to find a way to specify the total latency of a Recording/Playback system. But I don't think this flag will do this as it is only related to the device's buffer size. Any idea how I could specify the system's total latency I want?

bastibe commented 6 years ago

The issue is that pulseaudio can change its internal blocksize at any time. All we can do is specify a desired latency, and hope that pulseaudio is willing to honor our request. As far as I know, there is no way around this. If we want low latency, we need to deal with changing block sizes.

Pulseaudio provides the current stream latency in pa_stream_get_latency. This is not currently exposed, but should be easy to add. Also, there is an overview article that explains a way to configure pulseaudio for minimum latency. You could try out that recommendation as well.

mpariente commented 6 years ago

Thanks for the overview article, this is what I was reading when I realized the potential problem. The paragraph "How to pick the latency" tells that, in our case, picking a blocksize much smaller than numframes would result in unnecessary CPU burn. Do I understand correctly ?

I'll make a PR to try to integrate it if it's ok for you.

bastibe commented 6 years ago

I think right now we are setting the desired latency to be exactly one block size. This can be a problem if pulseaudio decides to provide slightly more or less data than requested, in which case SoundCard will buffer one additional block before returning. This problem can be lessened by using a smaller desired latency, though at the cost of some CPU churn. I don't think there is a solution for this on our end.

I don't quite understand yet, what you are trying to integrate, but I'll be very interested in your pull request! Any further insight into this topic will be very interesting!

mpariente commented 6 years ago

Sorry I was not clear about wanting to integrate pa_stream_get_latency (New PR here #18)

.

I think right now we are setting the desired latency to be exactly one block size. This can be a problem if pulseaudio decides to provide slightly more or less data than requested, in which case SoundCard will buffer one additional block before returning.

If pulseaudio provides more data than requested it's not a bad thing, we don't get optimal latency because we waited a little too much to get the data but we can return it directly, and in case the following one has less data than requested, it can even compensate.

I'll look into the distribution of returned data lengths in function of requested block size when I have time, there is probably an optimal ratio to find.