]> git.sesse.net Git - nageru/blobdiff - nageru/audio_mixer.h
When the delay analyzer wants audio from an ALSA card, temporarily auto-enable captur...
[nageru] / nageru / audio_mixer.h
index 9793646c9bac3493651e39643a610c7df75c5d0f..fac2fc92445420901ef084043356670d6a250bf7 100644 (file)
@@ -6,7 +6,8 @@
 // processing them with effects (if desired), and then mixing them
 // all together into one final audio signal.
 //
-// All operations on AudioMixer (except destruction) are thread-safe.
+// All operations on AudioMixer, except destruction and set_delay_analyzer(),
+// are thread-safe.
 
 #include <assert.h>
 #include <stdint.h>
@@ -23,7 +24,7 @@
 
 #include "alsa_pool.h"
 #include "correlation_measurer.h"
-#include "db.h"
+#include "decibel.h"
 #include "defs.h"
 #include "ebu_r128_proc.h"
 #include "filter.h"
 #include "resampling_queue.h"
 #include "stereocompressor.h"
 
+class DelayAnalyzerInterface;
 class DeviceSpecProto;
 
 namespace bmusb {
 struct AudioFormat;
 }  // namespace bmusb
 
+// Convert the given audio from {16,24,32}-bit M-channel to 32-bit N-channel PCM.
+// Assumes little-endian and chunky, signed PCM throughout.
+std::vector<int32_t> convert_audio_to_fixed32(const uint8_t *data, unsigned num_samples, bmusb::AudioFormat audio_format, unsigned num_destination_channels);
+
+// Similar, except converts to floating-point instead, and converts only one channel.
+void convert_audio_to_fp32(float *dst, size_t out_channel, size_t out_num_channels,
+                           const uint8_t *src, size_t in_channel, bmusb::AudioFormat in_audio_format,
+                           size_t num_samples);
+
 enum EQBand {
        EQ_BAND_BASS = 0,
        EQ_BAND_MID,
@@ -54,9 +65,9 @@ public:
        // the lock wasn't successfully taken; if so, you should simply try again.
        // (This is to avoid a deadlock where a card hangs on the mutex in add_audio()
        // while we are trying to shut it down from another thread that also holds
-       // the mutex.) frame_length is in TIMEBASE units.
-       bool add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned num_samples, bmusb::AudioFormat audio_format, int64_t frame_length, std::chrono::steady_clock::time_point frame_time);
-       bool add_silence(DeviceSpec device_spec, unsigned samples_per_frame, unsigned num_frames, int64_t frame_length);
+       // the mutex.)
+       bool add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned num_samples, bmusb::AudioFormat audio_format, std::chrono::steady_clock::time_point frame_time);
+       bool add_silence(DeviceSpec device_spec, unsigned samples_per_frame, unsigned num_frames);
 
        // If a given device is offline for whatever reason and cannot deliver audio
        // (by means of add_audio() or add_silence()), you can call put it in silence mode,
@@ -73,10 +84,15 @@ public:
        bool get_mute(unsigned bus_index) const { return mute[bus_index]; }
        void set_mute(unsigned bus_index, bool muted) { mute[bus_index] = muted; }
 
-       // Note: This operation holds all ALSA devices (see ALSAPool::get_devices()).
-       // You will need to call set_input_mapping() to get the hold state correctly,
-       // or every card will be held forever.
-       std::map<DeviceSpec, DeviceInfo> get_devices();
+       enum HoldDevices {
+               HOLD_NO_DEVICES,
+
+               // Note: Holds all ALSA devices (see ALSAPool::get_devices()).
+               // You will need to call set_input_mapping() to get the hold state correctly,
+               // or every card will be held forever.
+               HOLD_ALSA_DEVICES
+       };
+       std::map<DeviceSpec, DeviceInfo> get_devices(HoldDevices hold_devices);
 
        // See comments on ALSAPool::get_card_state().
        ALSAPool::Device::State get_alsa_card_state(unsigned index)
@@ -120,6 +136,9 @@ public:
        MappingMode get_mapping_mode() const;
        InputMapping get_input_mapping() const;
 
+       // See extra_devices.
+       void set_extra_devices(const std::set<DeviceSpec> &devices);
+
        unsigned num_buses() const;
 
        void set_locut_cutoff(float cutoff_hz)
@@ -208,51 +227,51 @@ public:
 
        void set_gain_staging_db(unsigned bus_index, float gain_db)
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                level_compressor_enabled[bus_index] = false;
                gain_staging_db[bus_index] = gain_db;
        }
 
        float get_gain_staging_db(unsigned bus_index) const
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                return gain_staging_db[bus_index];
        }
 
        void set_gain_staging_auto(unsigned bus_index, bool enabled)
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                level_compressor_enabled[bus_index] = enabled;
        }
 
        bool get_gain_staging_auto(unsigned bus_index) const
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                return level_compressor_enabled[bus_index];
        }
 
        void set_final_makeup_gain_db(float gain_db)
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                final_makeup_gain_auto = false;
                final_makeup_gain = from_db(gain_db);
        }
 
        float get_final_makeup_gain_db()
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                return to_db(final_makeup_gain);
        }
 
        void set_final_makeup_gain_auto(bool enabled)
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                final_makeup_gain_auto = enabled;
        }
 
        bool get_final_makeup_gain_auto() const
        {
-               std::unique_lock<std::mutex> lock(compressor_mutex);
+               std::lock_guard<std::mutex> lock(compressor_mutex);
                return final_makeup_gain_auto;
        }
 
@@ -312,6 +331,12 @@ public:
        BusSettings get_bus_settings(unsigned bus_index) const;
        void set_bus_settings(unsigned bus_index, const BusSettings &settings);
 
+       // Does not take ownership. Not thread-safe (so only call when the mixer is being created).
+       void set_delay_analyzer(DelayAnalyzerInterface *delay_analyzer)
+       {
+               this->delay_analyzer = delay_analyzer;
+       }
+
 private:
        struct AudioDevice {
                std::unique_ptr<ResamplingQueue> resampling_queue;
@@ -320,6 +345,10 @@ private:
                // Which channels we consider interesting (ie., are part of some input_mapping).
                std::set<unsigned> interesting_channels;
                bool silenced = false;
+
+               // Positive means the audio is delayed, negative means we try to have it earlier
+               // (although we can't time-travel!). Stored together with the input mapping.
+               double extra_delay_ms = 0.0;
        };
 
        const AudioDevice *find_audio_device(DeviceSpec device_spec) const
@@ -339,6 +368,7 @@ private:
        void send_audio_level_callback();
        std::vector<DeviceSpec> get_active_devices() const;
        void set_input_mapping_lock_held(const InputMapping &input_mapping);
+       void start_or_stop_alsa_capture(DeviceSpec device_spec);
 
        unsigned num_capture_cards, num_ffmpeg_inputs;
 
@@ -422,6 +452,15 @@ private:
                std::atomic<double> compressor_attenuation_db{0.0/0.0};
        };
        std::unique_ptr<BusMetrics[]> bus_metrics;  // One for each bus in <input_mapping>.
+
+       DelayAnalyzerInterface *delay_analyzer = nullptr;
+
+       // A set of devices (potentially empty) that should be kept open even
+       // if they're not used in any bus. This allows the delay analyzer to
+       // make sure a given ALSA device is opened to tap into its data, even if
+       // there is no bus using it. (Non-ALSA devices are allowed to be here,
+       // but won't do anything.)
+       std::set<DeviceSpec> extra_devices;
 };
 
 extern AudioMixer *global_audio_mixer;