]> git.sesse.net Git - nageru/commitdiff
Add support for ALSA inputs to the mixer.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 13 Aug 2016 12:29:01 +0000 (14:29 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 19 Oct 2016 22:55:44 +0000 (00:55 +0200)
audio_mixer.cpp
audio_mixer.h
defs.h
input_mapping_dialog.cpp

index c1f74107ff382403f9112e04f7d312a65185d3ac..acd48c5e0903775bcb598d452f3bcb28e04225ef 100644 (file)
@@ -13,6 +13,7 @@
 
 using namespace bmusb;
 using namespace std;
+using namespace std::placeholders;
 
 namespace {
 
@@ -104,8 +105,22 @@ AudioMixer::AudioMixer(unsigned num_cards)
        InputMapping new_input_mapping;
        new_input_mapping.buses.push_back(input);
        set_input_mapping(new_input_mapping);
+
+       // Look for ALSA cards.
+       available_alsa_cards = ALSAInput::enumerate_devices();
+}
+
+AudioMixer::~AudioMixer()
+{
+       for (unsigned card_index = 0; card_index < available_alsa_cards.size(); ++card_index) {
+               const AudioDevice &device = alsa_inputs[card_index];
+               if (device.alsa_device != nullptr) {
+                       device.alsa_device->stop_capture_thread();
+               }
+       }
 }
 
+
 void AudioMixer::reset_resampler(DeviceSpec device_spec)
 {
        lock_guard<mutex> lock(audio_mutex);
@@ -126,6 +141,24 @@ void AudioMixer::reset_resampler_mutex_held(DeviceSpec device_spec)
        device->next_local_pts = 0;
 }
 
+void AudioMixer::reset_alsa_mutex_held(DeviceSpec device_spec)
+{
+       assert(device_spec.type == InputSourceType::ALSA_INPUT);
+       unsigned card_index = device_spec.index;
+       AudioDevice *device = find_audio_device(device_spec);
+
+       if (device->alsa_device != nullptr) {
+               device->alsa_device->stop_capture_thread();
+       }
+       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)));
+               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)
 {
        AudioDevice *device = find_audio_device(device_spec);
@@ -197,6 +230,8 @@ AudioMixer::AudioDevice *AudioMixer::find_audio_device(DeviceSpec device)
        switch (device.type) {
        case InputSourceType::CAPTURE_CARD:
                return &video_cards[device.index];
+       case InputSourceType::ALSA_INPUT:
+               return &alsa_inputs[device.index];
        case InputSourceType::SILENCE:
        default:
                assert(false);
@@ -204,6 +239,8 @@ AudioMixer::AudioDevice *AudioMixer::find_audio_device(DeviceSpec device)
        return nullptr;
 }
 
+// Get a pointer to the given channel from the given device.
+// The channel must be picked out earlier and resampled.
 void AudioMixer::find_sample_src_from_device(const map<DeviceSpec, vector<float>> &samples_card, DeviceSpec device_spec, int source_channel, const float **srcptr, unsigned *stride)
 {
        static float zero = 0.0f;
@@ -213,6 +250,7 @@ void AudioMixer::find_sample_src_from_device(const map<DeviceSpec, vector<float>
                return;
        }
        AudioDevice *device = find_audio_device(device_spec);
+       assert(device->interesting_channels.count(source_channel) != 0);
        unsigned channel_index = 0;
        for (int channel : device->interesting_channels) {
                if (channel == source_channel) break;
@@ -231,7 +269,8 @@ void AudioMixer::fill_audio_bus(const map<DeviceSpec, vector<float>> &samples_ca
        if (bus.device.type == InputSourceType::SILENCE) {
                memset(output, 0, num_samples * sizeof(*output));
        } else {
-               assert(bus.device.type == InputSourceType::CAPTURE_CARD);
+               assert(bus.device.type == InputSourceType::CAPTURE_CARD ||
+                      bus.device.type == InputSourceType::ALSA_INPUT);
                const float *lsrc, *rsrc;
                unsigned lstride, rstride;
                float *dptr = output;
@@ -419,6 +458,14 @@ map<DeviceSpec, DeviceInfo> AudioMixer::get_devices_mutex_held() const
                info.num_channels = 8;  // FIXME: This is wrong for fake cards.
                devices.insert(make_pair(spec, info));
        }
+       for (unsigned card_index = 0; card_index < available_alsa_cards.size(); ++card_index) {
+               const DeviceSpec spec{ InputSourceType::ALSA_INPUT, card_index };
+               const ALSAInput::Device &device = available_alsa_cards[card_index];
+               DeviceInfo info;
+               info.name = device.name + " (" + device.info + ")";
+               info.num_channels = device.num_channels;
+               devices.insert(make_pair(spec, info));
+       }
        return devices;
 }
 
@@ -436,7 +483,8 @@ void AudioMixer::set_input_mapping(const InputMapping &new_input_mapping)
 
        map<DeviceSpec, set<unsigned>> interesting_channels;
        for (const InputMapping::Bus &bus : new_input_mapping.buses) {
-               if (bus.device.type == InputSourceType::CAPTURE_CARD) {
+               if (bus.device.type == InputSourceType::CAPTURE_CARD ||
+                   bus.device.type == InputSourceType::ALSA_INPUT) {
                        for (unsigned channel = 0; channel < 2; ++channel) {
                                if (bus.source_channel[channel] != -1) {
                                        interesting_channels[bus.device].insert(bus.source_channel[channel]);
@@ -451,6 +499,9 @@ void AudioMixer::set_input_mapping(const InputMapping &new_input_mapping)
                AudioDevice *device = find_audio_device(device_spec);
                if (device->interesting_channels != interesting_channels[device_spec]) {
                        device->interesting_channels = interesting_channels[device_spec];
+                       if (device_spec.type == InputSourceType::ALSA_INPUT) {
+                               reset_alsa_mutex_held(device_spec);
+                       }
                        reset_resampler_mutex_held(device_spec);
                }
        }
index 30675b88b6f8bfbb5cddbaf635bee42ebb97da33..23b020d43f77bb5765fd14efe55f18b588f6d75d 100644 (file)
@@ -20,6 +20,7 @@
 #include <set>
 #include <vector>
 
+#include "alsa_input.h"
 #include "bmusb/bmusb.h"
 #include "db.h"
 #include "defs.h"
@@ -31,7 +32,7 @@ namespace bmusb {
 struct AudioFormat;
 }  // namespace bmusb
 
-enum class InputSourceType { SILENCE, CAPTURE_CARD };
+enum class InputSourceType { SILENCE, CAPTURE_CARD, ALSA_INPUT };
 struct DeviceSpec {
        InputSourceType type;
        unsigned index;
@@ -74,6 +75,7 @@ struct InputMapping {
 class AudioMixer {
 public:
        AudioMixer(unsigned num_cards);
+       ~AudioMixer();
        void reset_resampler(DeviceSpec device_spec);
 
        // frame_length is in TIMEBASE units.
@@ -204,12 +206,15 @@ private:
                unsigned capture_frequency = OUTPUT_FREQUENCY;
                // Which channels we consider interesting (ie., are part of some input_mapping).
                std::set<unsigned> interesting_channels;
+               // Only used for ALSA cards, obviously.
+               std::unique_ptr<ALSAInput> alsa_device;
        };
        AudioDevice *find_audio_device(DeviceSpec device_spec);
 
        void find_sample_src_from_device(const std::map<DeviceSpec, std::vector<float>> &samples_card, DeviceSpec device_spec, int source_channel, const float **srcptr, unsigned *stride);
        void fill_audio_bus(const std::map<DeviceSpec, std::vector<float>> &samples_card, const InputMapping::Bus &bus, unsigned num_samples, float *output);
        void reset_resampler_mutex_held(DeviceSpec device_spec);
+       void reset_alsa_mutex_held(DeviceSpec device_spec);
        std::map<DeviceSpec, DeviceInfo> get_devices_mutex_held() const;
 
        unsigned num_cards;
@@ -218,6 +223,10 @@ private:
 
        AudioDevice video_cards[MAX_VIDEO_CARDS];  // Under audio_mutex.
 
+       // TODO: Figure out a better way to unify these two, as they are sharing indexing.
+       AudioDevice alsa_inputs[MAX_ALSA_CARDS];  // Under audio_mutex.
+       std::vector<ALSAInput::Device> available_alsa_cards;
+
        StereoFilter locut;  // Default cutoff 120 Hz, 24 dB/oct.
        std::atomic<float> locut_cutoff_hz;
        std::atomic<bool> locut_enabled{true};
diff --git a/defs.h b/defs.h
index 15e6f3cf6f12e83aaa868b89f227502bd8776aba..08708ad6dbf876404c5e969cc4c9ce37ea5037f7 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -7,6 +7,7 @@
 #define HEIGHT 720
 #define FAKE_FPS 25  // Must be an integer.
 #define MAX_VIDEO_CARDS 16
+#define MAX_ALSA_CARDS 16
 #define MAX_BUSES 256  // Audio buses.
 
 // For deinterlacing. See also comments on InputState.
index 2d40cc2a500a839557e46aef541fa1c52677a86f..9c8180cce6321750894c1b4547b6c6f991cbca37 100644 (file)
@@ -75,7 +75,8 @@ void InputMappingDialog::setup_channel_choices_from_bus(unsigned row, const Inpu
        for (unsigned channel = 0; channel < 2; ++channel) {
                QComboBox *channel_combo = new QComboBox;
                channel_combo->addItem(QString("(none)"));
-               if (bus.device.type == InputSourceType::CAPTURE_CARD) {
+               if (bus.device.type == InputSourceType::CAPTURE_CARD ||
+                   bus.device.type == InputSourceType::ALSA_INPUT) {
                        auto device_it = devices.find(bus.device);
                        assert(device_it != devices.end());
                        unsigned num_device_channels = device_it->second.num_channels;