[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

@@ -2156,7 +2156,7 @@ static void realRecordStartLocked(const LG_AudioFormat * format)
audio.audioDev->record.start(format, recordPushFrames);
// if a volume level was stored, set it before we return
if (audio.record.volumeChannels)
if (audio.record.volumeChannels && audio.audioDev->record.volume)
audio.audioDev->record.volume(
audio.record.volumeChannels,
audio.record.volume);
@@ -2201,7 +2201,8 @@ static void recordConfirm(bool yes, void * opaque)
static void recordStart(const LG_AudioFormat * format)
{
LG_LOCK(audio.record.lock);
if (!audio.audioDev || audio.record.shuttingDown ||
if (!audio.audioDev || !audio.audioDev->record.start ||
audio.record.shuttingDown ||
!audioFormatValid(format))
{
if (format && !audioFormatValid(format))
@@ -2271,6 +2272,29 @@ static void recordStart(const LG_AudioFormat * format)
app_msgBoxClose(oldConfirm);
}
static void recordReconfigure(const LG_AudioFormat * format)
{
LG_LOCK(audio.record.lock);
if (!audio.audioDev || !audio.audioDev->record.start ||
audio.record.shuttingDown || !audioFormatValid(format))
{
LG_UNLOCK(audio.record.lock);
return;
}
audio.record.lastFormat = *format;
if (audio.record.started &&
!audioFormatEqual(format, &audio.record.format))
{
realRecordStopLocked();
realRecordStartLocked(format);
}
else if (audio.record.confirmPending)
audio.record.confirmFormat = *format;
LG_UNLOCK(audio.record.lock);
}
static void realRecordStopLocked(void)
{
audio.audioDev->record.stop();
@@ -2522,9 +2546,12 @@ static void eventRecordStart(void * opaque, uint32_t generation,
binding->ops->recordData;
if (active)
{
atomic_store_explicit(&audio.record.streamGeneration,
generation, memory_order_release);
recordStart(format);
const uint32_t previous = atomic_exchange_explicit(
&audio.record.streamGeneration, generation, memory_order_acq_rel);
if (previous == generation)
recordReconfigure(format);
else
recordStart(format);
}
LG_UNLOCK_SHARED(audio.activeLock);
}