[client] usb audio: add microphone recording
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled

Add a composite UAC2 microphone function with its own clock so
recording and playback can use independent sample rates.

Packetize asynchronous capture through usbredir, follow the host
capture clock, and preserve the newest frames across stalls.

Reconfigure active recording without another permission prompt and
fix lost PipeWire capture wakeups.
This commit is contained in:
Geoffrey McRae
2026-08-10 06:32:05 +10:00
parent d660574a93
commit 4434985aa3
10 changed files with 862 additions and 200 deletions

View File

@@ -915,7 +915,10 @@ static void pipewire_recordQueueFrames(const void * data, int frames)
atomic_fetch_add_explicit(
&pw.record.droppedFrames, frames - append, memory_order_relaxed);
if (occupancy == 0 && append > 0 && sem_post(&pw.record.sendWake) < 0)
/* Posting only when the producer observes an empty queue can lose a wake
* while the consumer drains the previous batch. Always notify for appended
* data; the sender coalesces all queued frames when it wakes. */
if (append > 0 && sem_post(&pw.record.sendWake) < 0)
atomic_fetch_add_explicit(
&pw.record.signalErrors, 1, memory_order_relaxed);
}
@@ -961,9 +964,13 @@ static void pipewire_onRecordProcess(void * userdata)
static void pipewire_recordStart(const LG_AudioFormat * format,
LG_AudioPushFn pushFn)
{
const int channels = format->channelCount;
const int sampleRate = format->sampleRate;
const int sampleSize = pipewire_sampleSize(format->sampleFormat);
const int channels = format->channelCount;
const int sampleRate = format->sampleRate;
const int sampleSize = pipewire_sampleSize(format->sampleFormat);
const int periodFrames = max(sampleRate / 1000, 1);
char requestedNodeLatency[32];
snprintf(requestedNodeLatency, sizeof(requestedNodeLatency), "%d/%d",
periodFrames, sampleRate);
const struct spa_pod * params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
@@ -1000,6 +1007,7 @@ static void pipewire_recordStart(const LG_AudioFormat * format,
PW_KEY_MEDIA_TYPE , "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE , "Music",
PW_KEY_NODE_LATENCY , requestedNodeLatency,
NULL
);
if (!props)