mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-22 13:33:40 +00:00
[client] audio: add latency tuning parameter
This adds a new `audio:bufferLatency` option which allows the user to adjust the amount of buffering LG does over the absolute bare minimum. By default, this is set large enough to absorb typical timing jitter from Spice. Users may reduce this if they care more about latency than audio quality.
This commit is contained in:
parent
9908b737b0
commit
7c2d493bb5
@ -517,16 +517,26 @@ void audio_playbackData(uint8_t * data, size_t size)
|
||||
spiceData->devNextPosition = deviceTick.nextPosition;
|
||||
}
|
||||
|
||||
// Determine the target latency. Ideally, this would be precisely equal to
|
||||
// the maximum device period size. However, we need to allow for some timing
|
||||
// jitter to avoid underruns. Packets from Spice in particular can sometimes
|
||||
// be delayed by an entire period or more, so include a fixed amount of
|
||||
// latency to absorb these gaps. For device jitter use a multiplier, so timing
|
||||
// requirements get progressively stricter as the period size is reduced
|
||||
int spiceJitterMs = 13;
|
||||
// Determine the target latency. This is made up of three components:
|
||||
// 1. Half the Spice period. This is necessary due to the way qemu handles
|
||||
// audio. Data is not sent as soon as it is produced by the virtual sound
|
||||
// card; instead, qemu polls for new data every ~10ms. This results in a
|
||||
// sawtooth pattern in the packet timing as it drifts in and out of phase
|
||||
// with the virtual device. LG measures the average progression of the
|
||||
// Spice clock, so sees the packet timing error drift by half a period
|
||||
// above and below the measured clock. We need to account for this in the
|
||||
// target latency to avoid underrunning.
|
||||
// 2. The maximum audio device period, plus a little extra to absorb timing
|
||||
// jitter.
|
||||
// 3. A configurable additional buffer period. The default value is set high
|
||||
// enough to absorb typical timing jitter from Spice, which can be quite
|
||||
// significant. Users may reduce this if they care more about latency than
|
||||
// audio quality.
|
||||
int configLatencyMs = max(g_params.audioBufferLatency, 0);
|
||||
double targetLatencyFrames =
|
||||
spiceJitterMs * audio.playback.sampleRate / 1000.0 +
|
||||
audio.playback.deviceMaxPeriodFrames * 1.1;
|
||||
spiceData->periodFrames / 2.0 +
|
||||
audio.playback.deviceMaxPeriodFrames * 1.1 +
|
||||
configLatencyMs * audio.playback.sampleRate / 1000.0;
|
||||
|
||||
// If the device is currently at a lower period size than its maximum (which
|
||||
// can happen, for example, if another application has requested a lower
|
||||
|
@ -473,6 +473,13 @@ static struct Option options[] =
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 2048
|
||||
},
|
||||
{
|
||||
.module = "audio",
|
||||
.name = "bufferLatency",
|
||||
.description = "Additional buffer latency in milliseconds",
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 8
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -646,6 +653,7 @@ bool config_load(int argc, char * argv[])
|
||||
}
|
||||
|
||||
g_params.audioPeriodSize = option_get_int("audio", "periodSize");
|
||||
g_params.audioBufferLatency = option_get_int("audio", "bufferLatency");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -201,6 +201,7 @@ struct AppParams
|
||||
bool showCursorDot;
|
||||
|
||||
int audioPeriodSize;
|
||||
int audioBufferLatency;
|
||||
};
|
||||
|
||||
struct CBRequest
|
||||
|
Loading…
Reference in New Issue
Block a user