]> git.sesse.net Git - nageru/blobdiff - audio_mixer.cpp
Ask for the right number of channels when creating an ALSA device.
[nageru] / audio_mixer.cpp
index acd48c5e0903775bcb598d452f3bcb28e04225ef..769dabfa74a0831cb6678a52f15d245d2c4ca6f2 100644 (file)
@@ -77,13 +77,38 @@ void convert_fixed32_to_fp32(float *dst, size_t out_channel, size_t out_num_chan
        }
 }
 
+float find_peak(const float *samples, size_t num_samples)
+{
+       float m = fabs(samples[0]);
+       for (size_t i = 1; i < num_samples; ++i) {
+               m = max(m, fabs(samples[i]));
+       }
+       return m;
+}
+
+void deinterleave_samples(const vector<float> &in, vector<float> *out_l, vector<float> *out_r)
+{
+       size_t num_samples = in.size() / 2;
+       out_l->resize(num_samples);
+       out_r->resize(num_samples);
+
+       const float *inptr = in.data();
+       float *lptr = &(*out_l)[0];
+       float *rptr = &(*out_r)[0];
+       for (size_t i = 0; i < num_samples; ++i) {
+               *lptr++ = *inptr++;
+               *rptr++ = *inptr++;
+       }
+}
+
 }  // namespace
 
 AudioMixer::AudioMixer(unsigned num_cards)
        : num_cards(num_cards),
          level_compressor(OUTPUT_FREQUENCY),
          limiter(OUTPUT_FREQUENCY),
-         compressor(OUTPUT_FREQUENCY)
+         compressor(OUTPUT_FREQUENCY),
+         correlation(OUTPUT_FREQUENCY)
 {
        locut.init(FILTER_HPF, 2);
 
@@ -108,6 +133,13 @@ AudioMixer::AudioMixer(unsigned num_cards)
 
        // Look for ALSA cards.
        available_alsa_cards = ALSAInput::enumerate_devices();
+
+       r128.init(2, OUTPUT_FREQUENCY);
+       r128.integr_start();
+
+       // hlen=16 is pretty low quality, but we use quite a bit of CPU otherwise,
+       // and there's a limit to how important the peak meter is.
+       peak_resampler.setup(OUTPUT_FREQUENCY, OUTPUT_FREQUENCY * 4, /*num_channels=*/2, /*hlen=*/16, /*frel=*/1.0);
 }
 
 AudioMixer::~AudioMixer()
@@ -123,7 +155,7 @@ AudioMixer::~AudioMixer()
 
 void AudioMixer::reset_resampler(DeviceSpec device_spec)
 {
-       lock_guard<mutex> lock(audio_mutex);
+       lock_guard<timed_mutex> lock(audio_mutex);
        reset_resampler_mutex_held(device_spec);
 }
 
@@ -153,20 +185,24 @@ void AudioMixer::reset_alsa_mutex_held(DeviceSpec device_spec)
        if (device->interesting_channels.empty()) {
                device->alsa_device.reset();
        } else {
-               device->alsa_device.reset(new ALSAInput(available_alsa_cards[card_index].address.c_str(), OUTPUT_FREQUENCY, 2, bind(&AudioMixer::add_audio, this, device_spec, _1, _2, _3, _4)));
+               const ALSAInput::Device &alsa_dev = available_alsa_cards[card_index];
+               device->alsa_device.reset(new ALSAInput(alsa_dev.address.c_str(), OUTPUT_FREQUENCY, alsa_dev.num_channels, bind(&AudioMixer::add_audio, this, device_spec, _1, _2, _3, _4)));
                device->capture_frequency = device->alsa_device->get_sample_rate();
                device->alsa_device->start_capture_thread();
        }
 }
 
-void AudioMixer::add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned num_samples, AudioFormat audio_format, int64_t frame_length)
+bool AudioMixer::add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned num_samples, AudioFormat audio_format, int64_t frame_length)
 {
        AudioDevice *device = find_audio_device(device_spec);
 
-       lock_guard<mutex> lock(audio_mutex);
+       unique_lock<timed_mutex> lock(audio_mutex, defer_lock);
+       if (!lock.try_lock_for(chrono::milliseconds(10))) {
+               return false;
+       }
        if (device->resampling_queue == nullptr) {
                // No buses use this device; throw it away.
-               return;
+               return true;
        }
 
        unsigned num_channels = device->interesting_channels.size();
@@ -200,16 +236,20 @@ void AudioMixer::add_audio(DeviceSpec device_spec, const uint8_t *data, unsigned
        int64_t local_pts = device->next_local_pts;
        device->resampling_queue->add_input_samples(local_pts / double(TIMEBASE), audio.data(), num_samples);
        device->next_local_pts = local_pts + frame_length;
+       return true;
 }
 
-void AudioMixer::add_silence(DeviceSpec device_spec, unsigned samples_per_frame, unsigned num_frames, int64_t frame_length)
+bool AudioMixer::add_silence(DeviceSpec device_spec, unsigned samples_per_frame, unsigned num_frames, int64_t frame_length)
 {
        AudioDevice *device = find_audio_device(device_spec);
 
-       lock_guard<mutex> lock(audio_mutex);
+       unique_lock<timed_mutex> lock(audio_mutex, defer_lock);
+       if (!lock.try_lock_for(chrono::milliseconds(10))) {
+               return false;
+       }
        if (device->resampling_queue == nullptr) {
                // No buses use this device; throw it away.
-               return;
+               return true;
        }
 
        unsigned num_channels = device->interesting_channels.size();
@@ -223,6 +263,7 @@ void AudioMixer::add_silence(DeviceSpec device_spec, unsigned samples_per_frame,
                // is always the same.
                device->next_local_pts += frame_length;
        }
+       return true;
 }
 
 AudioMixer::AudioDevice *AudioMixer::find_audio_device(DeviceSpec device)
@@ -290,7 +331,7 @@ vector<float> AudioMixer::get_output(double pts, unsigned num_samples, Resamplin
        map<DeviceSpec, vector<float>> samples_card;
        vector<float> samples_bus;
 
-       lock_guard<mutex> lock(audio_mutex);
+       lock_guard<timed_mutex> lock(audio_mutex);
 
        // Pick out all the interesting channels from all the cards.
        // TODO: If the card has been hotswapped, the number of channels
@@ -310,12 +351,16 @@ vector<float> AudioMixer::get_output(double pts, unsigned num_samples, Resamplin
        }
 
        // TODO: Move lo-cut etc. into each bus.
-       vector<float> samples_out;
+       vector<float> samples_out, left, right;
        samples_out.resize(num_samples * 2);
        samples_bus.resize(num_samples * 2);
        for (unsigned bus_index = 0; bus_index < input_mapping.buses.size(); ++bus_index) {
                fill_audio_bus(samples_card, input_mapping.buses[bus_index], num_samples, &samples_bus[0]);
 
+               // TODO: We should measure post-fader.
+               deinterleave_samples(samples_bus, &left, &right);
+               measure_bus_levels(bus_index, left, right);
+
                float volume = from_db(fader_volume_db[bus_index]);
                if (bus_index == 0) {
                        for (unsigned i = 0; i < num_samples * 2; ++i) {
@@ -410,7 +455,7 @@ vector<float> AudioMixer::get_output(double pts, unsigned num_samples, Resamplin
        // Note that there's a feedback loop here, so we choose a very slow filter
        // (half-time of 30 seconds).
        double target_loudness_factor, alpha;
-       double loudness_lu = loudness_lufs - ref_level_lufs;
+       double loudness_lu = r128.loudness_M() - ref_level_lufs;
        double current_makeup_lu = to_db(final_makeup_gain);
        target_loudness_factor = final_makeup_gain * from_db(-loudness_lu);
 
@@ -438,12 +483,92 @@ vector<float> AudioMixer::get_output(double pts, unsigned num_samples, Resamplin
                final_makeup_gain = m;
        }
 
+       update_meters(samples_out);
+
        return samples_out;
 }
 
+void AudioMixer::measure_bus_levels(unsigned bus_index, const vector<float> &left, const vector<float> &right)
+{
+       const float *ptrs[] = { left.data(), right.data() };
+       {
+               lock_guard<mutex> lock(audio_measure_mutex);
+               bus_r128[bus_index]->process(left.size(), const_cast<float **>(ptrs));
+       }
+}
+
+void AudioMixer::update_meters(const vector<float> &samples)
+{
+       // Upsample 4x to find interpolated peak.
+       peak_resampler.inp_data = const_cast<float *>(samples.data());
+       peak_resampler.inp_count = samples.size() / 2;
+
+       vector<float> interpolated_samples;
+       interpolated_samples.resize(samples.size());
+       {
+               lock_guard<mutex> lock(audio_measure_mutex);
+
+               while (peak_resampler.inp_count > 0) {  // About four iterations.
+                       peak_resampler.out_data = &interpolated_samples[0];
+                       peak_resampler.out_count = interpolated_samples.size() / 2;
+                       peak_resampler.process();
+                       size_t out_stereo_samples = interpolated_samples.size() / 2 - peak_resampler.out_count;
+                       peak = max<float>(peak, find_peak(interpolated_samples.data(), out_stereo_samples * 2));
+                       peak_resampler.out_data = nullptr;
+               }
+       }
+
+       // Find R128 levels and L/R correlation.
+       vector<float> left, right;
+       deinterleave_samples(samples, &left, &right);
+       float *ptrs[] = { left.data(), right.data() };
+       {
+               lock_guard<mutex> lock(audio_measure_mutex);
+               r128.process(left.size(), ptrs);
+               correlation.process_samples(samples);
+       }
+
+       send_audio_level_callback();
+}
+
+void AudioMixer::reset_meters()
+{
+       lock_guard<mutex> lock(audio_measure_mutex);
+       peak_resampler.reset();
+       peak = 0.0f;
+       r128.reset();
+       r128.integr_start();
+       correlation.reset();
+}
+
+void AudioMixer::send_audio_level_callback()
+{
+       if (audio_level_callback == nullptr) {
+               return;
+       }
+
+       lock_guard<mutex> lock(audio_measure_mutex);
+       double loudness_s = r128.loudness_S();
+       double loudness_i = r128.integrated();
+       double loudness_range_low = r128.range_min();
+       double loudness_range_high = r128.range_max();
+
+       vector<float> bus_loudness;
+       bus_loudness.resize(input_mapping.buses.size());
+       for (unsigned bus_index = 0; bus_index < bus_r128.size(); ++bus_index) {
+               bus_loudness[bus_index] = bus_r128[bus_index]->loudness_S();
+       }
+
+       audio_level_callback(loudness_s, to_db(peak), bus_loudness,
+               loudness_i, loudness_range_low, loudness_range_high,
+               gain_staging_db,
+               to_db(final_makeup_gain),
+               correlation.get_correlation());
+}
+
 map<DeviceSpec, DeviceInfo> AudioMixer::get_devices() const
 {
-       lock_guard<mutex> lock(audio_mutex);
+       lock_guard<timed_mutex> lock(audio_mutex);
        return get_devices_mutex_held();
 }
 
@@ -473,13 +598,13 @@ void AudioMixer::set_name(DeviceSpec device_spec, const string &name)
 {
        AudioDevice *device = find_audio_device(device_spec);
 
-       lock_guard<mutex> lock(audio_mutex);
+       lock_guard<timed_mutex> lock(audio_mutex);
        device->name = name;
 }
 
 void AudioMixer::set_input_mapping(const InputMapping &new_input_mapping)
 {
-       lock_guard<mutex> lock(audio_mutex);
+       lock_guard<timed_mutex> lock(audio_mutex);
 
        map<DeviceSpec, set<unsigned>> interesting_channels;
        for (const InputMapping::Bus &bus : new_input_mapping.buses) {
@@ -506,11 +631,22 @@ void AudioMixer::set_input_mapping(const InputMapping &new_input_mapping)
                }
        }
 
+       {
+               lock_guard<mutex> lock(audio_measure_mutex);
+               bus_r128.resize(new_input_mapping.buses.size());
+               for (unsigned bus_index = 0; bus_index < bus_r128.size(); ++bus_index) {
+                       if (bus_r128[bus_index] == nullptr) {
+                               bus_r128[bus_index].reset(new Ebu_r128_proc);
+                       }
+                       bus_r128[bus_index]->init(2, OUTPUT_FREQUENCY);
+               }
+       }
+
        input_mapping = new_input_mapping;
 }
 
 InputMapping AudioMixer::get_input_mapping() const
 {
-       lock_guard<mutex> lock(audio_mutex);
+       lock_guard<timed_mutex> lock(audio_mutex);
        return input_mapping;
 }