X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Faudio_mixer.h;h=fac2fc92445420901ef084043356670d6a250bf7;hb=refs%2Fheads%2Faudio-delay;hp=9793646c9bac3493651e39643a610c7df75c5d0f;hpb=392f9d1ccb835c05a3874c4bea163788b2c37024;p=nageru diff --git a/nageru/audio_mixer.h b/nageru/audio_mixer.h index 9793646..fac2fc9 100644 --- a/nageru/audio_mixer.h +++ b/nageru/audio_mixer.h @@ -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 #include @@ -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" @@ -31,12 +32,22 @@ #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 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 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 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 &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 lock(compressor_mutex); + std::lock_guard 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 lock(compressor_mutex); + std::lock_guard lock(compressor_mutex); return gain_staging_db[bus_index]; } void set_gain_staging_auto(unsigned bus_index, bool enabled) { - std::unique_lock lock(compressor_mutex); + std::lock_guard lock(compressor_mutex); level_compressor_enabled[bus_index] = enabled; } bool get_gain_staging_auto(unsigned bus_index) const { - std::unique_lock lock(compressor_mutex); + std::lock_guard lock(compressor_mutex); return level_compressor_enabled[bus_index]; } void set_final_makeup_gain_db(float gain_db) { - std::unique_lock lock(compressor_mutex); + std::lock_guard lock(compressor_mutex); final_makeup_gain_auto = false; final_makeup_gain = from_db(gain_db); } float get_final_makeup_gain_db() { - std::unique_lock lock(compressor_mutex); + std::lock_guard lock(compressor_mutex); return to_db(final_makeup_gain); } void set_final_makeup_gain_auto(bool enabled) { - std::unique_lock lock(compressor_mutex); + std::lock_guard lock(compressor_mutex); final_makeup_gain_auto = enabled; } bool get_final_makeup_gain_auto() const { - std::unique_lock lock(compressor_mutex); + std::lock_guard 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 resampling_queue; @@ -320,6 +345,10 @@ private: // Which channels we consider interesting (ie., are part of some input_mapping). std::set 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 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 compressor_attenuation_db{0.0/0.0}; }; std::unique_ptr bus_metrics; // One for each bus in . + + 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 extra_devices; }; extern AudioMixer *global_audio_mixer;