summaryrefslogtreecommitdiffstats
path: root/configure.ac
diff options
context:
space:
mode:
authorAnton <anton@picapica.im>2026-08-24 05:06:23 +0400
committerAnton <anton@picapica.im>2026-08-24 05:06:23 +0400
commita4ddbc49c82e31ee980ea1096cc737db438c012e (patch)
tree045ae5a4031f928dedb772a8998b220f0d5aa3e2 /configure.ac
parent5c5fe2b41da2f4f1da32b12909a9b982e5dec70a (diff)
downloadpica-pica-a4ddbc49c82e31ee980ea1096cc737db438c012e.tar.gz
pica-pica-a4ddbc49c82e31ee980ea1096cc737db438c012e.zip
pica-client: echo cancellation, and native audio on Windows and macOS
PlatformDriverName() now takes a direction, and answers with one of two kinds of name. A bare one - "alsa", "pulse", "oss" - is an FFmpeg libavdevice driver opened the way everything else here is opened. One prefixed "platform-" is not an FFmpeg driver at all: what follows names a platform audio API the caller drives itself through NativeAudioBackend. Windows and macOS take the second route, because both provide acoustic echo cancellation but only to streams that declare themselves part of a voice call - AudioCategory_Communications on WASAPI, the VoiceProcessingIO audio unit on CoreAudio - and libavdevice's wrappers have no way to ask for either. Doing our own cancelling on platforms that would have done it better below us is not a trade worth making. Where the platform will not do it, speexdsp does: one EchoCanceller per call, shared by both directions through a QSharedPointer, since the call is torn down without joining either audio thread. Playback feeds it the far end as it hands it to the device; capture subtracts before encoding. Alignment is left to the FIFO - in steady state its backlog settles at the sound card's buffer depth, which is also how far ahead of the speaker the playback side runs, leaving only the room for the filter tail to model. Measured 27 dB ERLE on a 60 ms echo with 1.6 dB of loss on speech when nothing is playing. On macOS the canceller is a single audio unit that renders and captures at once, so both directions share one refcounted instance. Ringtones deliberately get a plain HALOutput unit instead - opening a voice unit to play a bell would switch the microphone on and prompt for permission. Linux gains a choice of alsa or pulse, with PulseAudio device enumeration through libpulse when configure finds it. Info.plist gains NSMicrophoneUsageDescription and NSCameraUsageDescription. Without them macOS 10.14 and later kills the process on first use of the microphone or camera rather than asking, so calls did not merely fail, they took the application with them. Two things fixed along the way that were not about echo at all: the settings dialog's microphone test paced playback by counting timer ticks, which on Windows meant a 20 ms packet every ~31 ms because the system timer granularity is 15.6 ms - the recording came out slowed down and chopped up. It now paces against a QElapsedTimer. And chooseEncoderSampleFmt() uses avcodec_get_supported_config() where available, the AVCodec format arrays it replaced having been removed in FFmpeg 9. Only the Linux paths are verified here; the Windows and macOS backends compile to nothing on this machine and have been reviewed by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CBqDgvq3Ty6C6BjLFH16bw
Diffstat (limited to 'configure.ac')
-rw-r--r--configure.ac53
1 files changed, 52 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 2db202c..aee28ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -92,6 +92,14 @@ AC_ARG_ENABLE([vaapi],
AS_HELP_STRING([--disable-vaapi],
[Disable VAAPI accelerated video encoding, decoding and rendering]))
+#checking if PulseAudio device enumeration is wanted. Only affects what the
+#settings dialog can list; see the check further down.
+AC_ARG_ENABLE([pulse],
+ AS_HELP_STRING([--enable-pulse],
+ [Enable listing of PulseAudio/PipeWire capture and playback devices (default: autodetect)])
+ AS_HELP_STRING([--disable-pulse],
+ [Disable listing of PulseAudio/PipeWire devices]))
+
#checking if the X11 rendering path is wanted
#Qt gives widgets a GLX context by default in an X11 session, and a GLX
#context has no way to import the DMA-BUF handles a VA surface is exported
@@ -134,7 +142,21 @@ AS_CASE([$host],
[
PKG_CHECK_MODULES([PLATFORM_DEPS],["alsa"])
],
- [*-w64-mingw*], [QT_DEPENDENCIES_LIBS="$QT_DEPENDENCIES_LIBS -mwindows"],
+ [*-w64-mingw*],
+ [
+ QT_DEPENDENCIES_LIBS="$QT_DEPENDENCIES_LIBS -mwindows"
+ #the WASAPI backend is COM: ole32 for CoCreateInstance and the
+ #task allocator, oleaut32 for PROPVARIANT.
+ PLATFORM_DEPS_LIBS="$PLATFORM_DEPS_LIBS -lole32 -loleaut32"
+ ],
+ [*-apple-darwin*],
+ [
+ #the CoreAudio backend: the HAL for device enumeration,
+ #AudioUnit/AudioToolbox for the VoiceProcessingIO unit that does
+ #the echo cancellation, CoreFoundation for the CFString handling
+ #the HAL's names come back as.
+ PLATFORM_DEPS_LIBS="$PLATFORM_DEPS_LIBS -framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework CoreFoundation"
+ ],
[]
)
AC_SUBST([QT_DEPENDENCIES_CFLAGS])
@@ -155,6 +177,35 @@ PKG_CHECK_MODULES([FFMPEG],["libavcodec libavdevice libavformat libavutil libswr
AC_SUBST([FFMPEG_CFLAGS])
AC_SUBST([FFMPEG_LIBS])
+#check for speexdsp, which provides the acoustic echo cancellation used in
+#calls. Not optional: on Linux there is no other cancellation in the picture
+#at all, and on the platforms that have their own it is still the fallback for
+#the endpoints and drivers that turn out not to.
+PKG_CHECK_MODULES([SPEEXDSP],[speexdsp])
+AC_SUBST([SPEEXDSP_CFLAGS])
+AC_SUBST([SPEEXDSP_LIBS])
+
+#check for libpulse, used only to list PulseAudio sources and sinks in the
+#settings dialog - playing and capturing through PulseAudio goes via FFmpeg's
+#own "pulse" device and needs nothing from us. Without it the "PulseAudio /
+#PipeWire" driver still works, but offers only the server's default device.
+AS_IF([test "x$enable_pulse" != "xno"], [
+ PKG_CHECK_MODULES([PULSE], [libpulse],
+ [
+ PICA_PULSE_ENABLED=yes
+ AC_DEFINE([HAVE_LIBPULSE], [1], [Define to 1 if PulseAudio device enumeration is available])
+ AC_SUBST([PULSE_CFLAGS])
+ AC_SUBST([PULSE_LIBS])
+ ],
+ [
+ AS_IF([test "x$enable_pulse" = "xyes"],
+ [AC_MSG_ERROR(["PulseAudio support was requested but libpulse was not found"])],
+ [AC_MSG_WARN(["libpulse not found - the PulseAudio driver will only offer the default device"])])
+ ])
+])
+
+AM_CONDITIONAL([PICA_PULSE], [test "x$PICA_PULSE_ENABLED" = "xyes"])
+
#check for VAAPI libraries, unless explicitly disabled
#libva is the accelerator itself, libva-drm opens it through a DRM render node,
#and EGL is what lets a decoded surface be handed to OpenGL without a copy.