mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-08-09 20:24:14 +00:00
[client] audio: make the requested audio device period size configurable
This adds a new `audio:periodSize` option which defaults to 2048 frames. For PipeWire, this controls the `PIPEWIRE_LATENCY` value. For PulseAudio, the controls the target buffer length (`tlength`) value.
This commit is contained in:

committed by
Geoffrey McRae

parent
0dad9b1e76
commit
9908b737b0
@@ -53,6 +53,7 @@ struct PipeWire
|
||||
int stride;
|
||||
LG_AudioPullFn pullFn;
|
||||
int maxPeriodFrames;
|
||||
int startFrames;
|
||||
|
||||
StreamState state;
|
||||
}
|
||||
@@ -185,7 +186,8 @@ static void pipewire_playbackStopStream(void)
|
||||
}
|
||||
|
||||
static void pipewire_playbackSetup(int channels, int sampleRate,
|
||||
int * maxPeriodFrames, LG_AudioPullFn pullFn)
|
||||
int requestedPeriodFrames, int * maxPeriodFrames, int * startFrames,
|
||||
LG_AudioPullFn pullFn)
|
||||
{
|
||||
const struct spa_pod * params[1];
|
||||
uint8_t buffer[1024];
|
||||
@@ -203,15 +205,15 @@ static void pipewire_playbackSetup(int channels, int sampleRate,
|
||||
pw.playback.sampleRate == sampleRate)
|
||||
{
|
||||
*maxPeriodFrames = pw.playback.maxPeriodFrames;
|
||||
*startFrames = pw.playback.startFrames;
|
||||
return;
|
||||
}
|
||||
|
||||
pipewire_playbackStopStream();
|
||||
|
||||
int defaultLatencyFrames = 2048;
|
||||
char defaultNodeLatency[32];
|
||||
snprintf(defaultNodeLatency, sizeof(defaultNodeLatency), "%d/%d",
|
||||
defaultLatencyFrames, sampleRate);
|
||||
char requestedNodeLatency[32];
|
||||
snprintf(requestedNodeLatency, sizeof(requestedNodeLatency), "%d/%d",
|
||||
requestedPeriodFrames, sampleRate);
|
||||
|
||||
pw.playback.channels = channels;
|
||||
pw.playback.sampleRate = sampleRate;
|
||||
@@ -227,7 +229,7 @@ static void pipewire_playbackSetup(int channels, int sampleRate,
|
||||
PW_KEY_MEDIA_TYPE , "Audio",
|
||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||
PW_KEY_MEDIA_ROLE , "Music",
|
||||
PW_KEY_NODE_LATENCY , defaultNodeLatency,
|
||||
PW_KEY_NODE_LATENCY , requestedNodeLatency,
|
||||
NULL
|
||||
),
|
||||
&events,
|
||||
@@ -250,21 +252,26 @@ static void pipewire_playbackSetup(int channels, int sampleRate,
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"PIPEWIRE_LATENCY value '%s' is invalid or does not match stream sample "
|
||||
"rate; defaulting to %d/%d", actualNodeLatency, defaultLatencyFrames,
|
||||
"rate; using %d/%d", actualNodeLatency, requestedPeriodFrames,
|
||||
sampleRate);
|
||||
|
||||
struct spa_dict_item items[] = {
|
||||
{ PW_KEY_NODE_LATENCY, defaultNodeLatency }
|
||||
{ PW_KEY_NODE_LATENCY, requestedNodeLatency }
|
||||
};
|
||||
pw_stream_update_properties(pw.playback.stream,
|
||||
&SPA_DICT_INIT_ARRAY(items));
|
||||
|
||||
pw.playback.maxPeriodFrames = defaultLatencyFrames;
|
||||
pw.playback.maxPeriodFrames = requestedPeriodFrames;
|
||||
}
|
||||
else
|
||||
pw.playback.maxPeriodFrames = num;
|
||||
|
||||
// If the previous quantum size was very small, PipeWire can request two full
|
||||
// periods almost immediately at the start of playback
|
||||
pw.playback.startFrames = pw.playback.maxPeriodFrames * 2;
|
||||
|
||||
*maxPeriodFrames = pw.playback.maxPeriodFrames;
|
||||
*startFrames = pw.playback.startFrames;
|
||||
|
||||
if (!pw.playback.stream)
|
||||
{
|
||||
|
@@ -39,6 +39,7 @@ struct PulseAudio
|
||||
bool sinkMuted;
|
||||
bool sinkStarting;
|
||||
int sinkMaxPeriodFrames;
|
||||
int sinkStartFrames;
|
||||
int sinkSampleRate;
|
||||
int sinkChannels;
|
||||
int sinkStride;
|
||||
@@ -257,29 +258,29 @@ static void pulseaudio_overflow_cb(pa_stream * p, void * userdata)
|
||||
}
|
||||
|
||||
static void pulseaudio_setup(int channels, int sampleRate,
|
||||
int * maxPeriodFrames, LG_AudioPullFn pullFn)
|
||||
int requestedPeriodFrames, int * maxPeriodFrames, int * startFrames,
|
||||
LG_AudioPullFn pullFn)
|
||||
{
|
||||
if (pa.sink && pa.sinkChannels == channels && pa.sinkSampleRate == sampleRate)
|
||||
{
|
||||
*maxPeriodFrames = pa.sinkMaxPeriodFrames;
|
||||
*startFrames = pa.sinkStartFrames;
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: be smarter about this
|
||||
const int PERIOD_LEN = 80;
|
||||
|
||||
pa_sample_spec spec = {
|
||||
.format = PA_SAMPLE_FLOAT32,
|
||||
.rate = sampleRate,
|
||||
.channels = channels
|
||||
};
|
||||
|
||||
int stride = channels * sizeof(float);
|
||||
int bufferSize = requestedPeriodFrames * 2 * stride;
|
||||
pa_buffer_attr attribs =
|
||||
{
|
||||
.maxlength = pa_usec_to_bytes((PERIOD_LEN * 2) * PA_USEC_PER_MSEC, &spec),
|
||||
.tlength = pa_usec_to_bytes(PERIOD_LEN * PA_USEC_PER_MSEC, &spec),
|
||||
.maxlength = -1,
|
||||
.tlength = bufferSize,
|
||||
.prebuf = 0,
|
||||
.fragsize = pa_usec_to_bytes(PERIOD_LEN * PA_USEC_PER_MSEC, &spec),
|
||||
.minreq = (uint32_t)-1
|
||||
};
|
||||
|
||||
@@ -295,17 +296,21 @@ static void pulseaudio_setup(int channels, int sampleRate,
|
||||
pa_stream_set_underflow_callback(pa.sink, pulseaudio_underflow_cb, NULL);
|
||||
pa_stream_set_overflow_callback (pa.sink, pulseaudio_overflow_cb , NULL);
|
||||
|
||||
pa_stream_connect_playback(pa.sink, NULL, &attribs,
|
||||
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
|
||||
pa_stream_connect_playback(pa.sink, NULL, &attribs, PA_STREAM_START_CORKED,
|
||||
NULL, NULL);
|
||||
|
||||
pa.sinkStride = channels * sizeof(float);
|
||||
pa.sinkStride = stride;
|
||||
pa.sinkPullFn = pullFn;
|
||||
pa.sinkMaxPeriodFrames = attribs.tlength / pa.sinkStride;
|
||||
pa.sinkMaxPeriodFrames = requestedPeriodFrames;
|
||||
pa.sinkCorked = true;
|
||||
pa.sinkStarting = false;
|
||||
|
||||
*maxPeriodFrames = pa.sinkMaxPeriodFrames;
|
||||
// If something else is, or was recently using a small latency value,
|
||||
// PulseAudio can request way more data at startup than is reasonable
|
||||
pa.sinkStartFrames = requestedPeriodFrames * 4;
|
||||
|
||||
*maxPeriodFrames = requestedPeriodFrames;
|
||||
*startFrames = pa.sinkStartFrames;
|
||||
|
||||
pa_threaded_mainloop_unlock(pa.loop);
|
||||
}
|
||||
|
Reference in New Issue
Block a user