Hey @DanB91, if possible can u share the code? I am having latency issues with ALSA and it wld help a lot
I opened a PCM device and the hardware params after initialization are as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | PCM handle name = 'default'
PCM state = PREPARED
access type = RW_INTERLEAVED
format = 'S16_LE' (Signed 16 bit Little Endian)
subformat = 'STD' (Standard)
channels = 2
rate = 48000 bps
period time = 33333 us
period size = 1600 frames
buffer time = 1000000 us
buffer size = 48000 frames
periods per buffer = 30 frames
exact rate = 48000/1 bps
significant bits = 16
|
I have set the hardware params such that
period time = seconds per game update(33ms for 30fps and 16ms for 60fps),
buffer size is 1 sec long and from these two quantities I set
periods per buffer = (buffer size/period size)
The logic to fill the buffer is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | local_persist int runningSample = 0;
local_persist int toneVolume = 3000;
local_persist int hz = 480;
int bytesPerWave = (bytesPerSample * samplesPerSecond)/hz;
int samplesPerWave = bytesPerWave/bytesPerSample;
int16 *currentSample = (int16*)soundBuffer;
currentSample = (int16*)soundBuffer;
snd_pcm_sframes_t expectedSamplesPerFrame = (int32)((real32)samplesPerSecond/(real32)framesPerSecond);
snd_pcm_sframes_t availableSamples;
snd_pcm_sframes_t pendingSamples;
snd_pcm_avail_delay(soundDeviceHandle, &availableSamples, &pendingSamples);
expectedSamplesPerFrame -= pendingSamples;
int frames = MINIMUM(expectedSamplesPerFrame, availableSamples);
for(int currentFrame = 0; currentFrame < frames; currentFrame++)
{
int16 sampleValue = toneVolume * Sine((real32)runningSample/(real32)samplesPerWave);
*currentSample++ = sampleValue;
*currentSample++ = sampleValue;
runningSample++;
}
char debugSound[1024] = {};
snprintf(debugSound, 1024, "\nPending: %.3fs, Avail: %.3fs, Expected: %.3fs Filling: frames: %.3fs\n---------\n", (real32)pendingSamples/(real32)samplesPerSecond, (real32)availableSamples/(real32)samplesPerSecond,(real32)expectedSamplesPerFrame/(real32)samplesPerSecond,(real32)frames/(real32)samplesPerSecond);
while((alsaReturnCode = snd_pcm_writei(soundDeviceHandle, soundBuffer, frames)) < 0)
{
if(alsaReturnCode == -EPIPE)
{
snprintf(debugSound, 1024, "Underflow: Preparing PCM. %s", snd_strerror(alsaReturnCode));
snd_pcm_prepare(soundDeviceHandle);
}
else
{
snprintf(debugSound, 1024, "%s", snd_strerror(alsaReturnCode));
snd_pcm_recover(soundDeviceHandle, alsaReturnCode, 0);
}
}
if (alsaReturnCode != (int)frames)
{
snprintf(debugSound,1024, "short write, write %d frames\n", alsaReturnCode);
}
|
Initially
expectedSamplesPerFrame is the 1 frame(i.e game update) worth of samples.
Then I query ALSA for
availableSamples and
pendingSamples.
Given
pendingSamples, I need to write
(expectedSamplesPerFrame - pendingSamples) samples to line up with the frame flip.
Finally the amount of
frames to write is the minimum of
(expectedSamplesPerFrame - pendingSamples) and
availableSamples.
When
expectedSamplesPerFrame is exactly 33ms worth of samples, I constantly get underruns.
| Pending: 0.000s, Avail: 1.000s, Expected: 0.033s, Filling: 0.033s -> (this happens for first few frames)
---------
Pending: 0.024s, Avail: 0.967s, Expected: 0.009s, Filling: 0.009s
---------
Pending: 0.009s, Avail: 0.991s, Expected: 0.024s, Filling: 0.024s -> (this happens for the rest of the program)
---------
|
So I tried increasing
expectedSamplesPerFrame to 2 frames worth of samples. Here after first few frames the
Pending gets stuck at 0.067s, due to which
Expected is set to 0s and thus the program doesn't write anything to the buffer for the rest of its execution. The write doesn't throw underrun or any other errors.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | Pending: 0.000s, Avail: 1.000s, Expected: 0.067s, Filling: 0.067s
---------
Pending: 0.040s, Avail: 0.977s, Expected: 0.027s, Filling: 0.027s
---------
Pending: 0.033s, Avail: 0.995s, Expected: 0.034s, Filling: 0.034s
---------
Pending: 0.033s, Avail: 0.961s, Expected: 0.033s, Filling: 0.033s
---------
Pending: 0.065s, Avail: 0.967s, Expected: 0.001s, Filling: 0.001s
---------
Pending: 0.067s, Avail: 0.999s, Expected: 0.000s, Filling: 0.000s -> (this happens for the rest of the program)
---------
|
Finally I tried filling whatever samples were available i.e
avaiableSamples, which resulted in lesser pop sounds but latency was very high.
1
2
3
4
5
6
7
8
9
10
11
12
13 | Pending: 0.000s, Avail: 1.000s, Expected: 1.000s Filling: frames: 1.000s
---------
Pending: 0.969s, Avail: 0.042s, Expected: 0.042s Filling: frames: 0.042s
---------
Pending: 0.978s, Avail: 0.044s, Expected: 0.044s Filling: frames: 0.044s
---------
Pending: 0.989s, Avail: 0.000s, Expected: 0.000s Filling: frames: 0.000s
---------
Pending: 0.956s, Avail: 0.046s, Expected: 0.046s Filling: frames: 0.046s
|
How do I decide the amount of samples to write so as to have minimum latency and lesser sound drops?
Cause I tried increasing
expectedSamplesPerFrame from 33ms to whatever was the max available in buffer but the issue persisted.