summaryrefslogtreecommitdiffstats
path: root/pica-client/capturegain.h
diff options
context:
space:
mode:
Diffstat (limited to 'pica-client/capturegain.h')
-rw-r--r--pica-client/capturegain.h69
1 files changed, 69 insertions, 0 deletions
diff --git a/pica-client/capturegain.h b/pica-client/capturegain.h
new file mode 100644
index 0000000..4d833f1
--- /dev/null
+++ b/pica-client/capturegain.h
@@ -0,0 +1,69 @@
+/*
+ (c) Copyright 2012 - 2026 Anton Sviridenko
+ https://picapica.im
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 3.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef CAPTUREGAIN_H
+#define CAPTUREGAIN_H
+
+#include <QString>
+
+// The capture device's own gain control - the mixer slider, not a multiplier
+// applied to samples we already have.
+//
+// The distinction is the entire point of this class. Clipping happens in the
+// converter, before any buffer reaches us; by then the peaks are flat and the
+// harmonics they generated are in the signal for good. Scaling that buffer
+// down produces a quieter clipped buffer and nothing else - in particular it
+// does not help the echo canceller, whose adaptive filter is linear and
+// therefore scale invariant. What defeats the filter is the non-linearity, and
+// the only place to prevent one is ahead of the ADC. That is this control.
+//
+// Gain is expressed as a 0.0 to 1.0 fraction of the control's travel, which is
+// what WASAPI and CoreAudio use natively and what ALSA and PulseAudio's ranges
+// map onto cleanly. The mapping from that fraction to decibels is the
+// platform's business and is not the same on all of them, so callers should
+// treat a step as "somewhat quieter" and close the loop by measuring again
+// rather than doing arithmetic in dB.
+//
+// Not thread safe, and deliberately so: AudioVideoCallController owns one of
+// these and touches it only from the GUI thread, driven by a queued signal
+// from the capture thread. Some of the implementations below need a message
+// loop or a COM apartment and have no business being called from an audio
+// callback.
+class CaptureGainControl
+{
+public:
+ virtual ~CaptureGainControl();
+
+ // Current position of the control, 0.0 to 1.0. False if it could not be
+ // read, in which case *out is untouched.
+ virtual bool gain(double *out) = 0;
+
+ // Moves the control. Values outside 0.0 to 1.0 are clamped.
+ virtual bool setGain(double value) = 0;
+
+ // What was actually found - the mixer element or endpoint name - for the
+ // log line that says the gain is being taken over.
+ virtual QString description() const = 0;
+
+ // driver is a name from AudioDevice::PlatformDriverName(), device the
+ // capture device string that goes with it. Returns null when this build
+ // or this platform has no way to reach the control, which is not an
+ // error: the caller carries on without automatic gain and the clipping
+ // warning still gets shown.
+ static CaptureGainControl *create(const QString &driver, const QString &device);
+};
+
+#endif // CAPTUREGAIN_H