#include <assert.h>
#include <bmusb/bmusb.h>
#include <endian.h>
+#include <map>
#include <math.h>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <string>
+#include <vector>
#ifdef __SSE2__
#include <immintrin.h>
#endif
+#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stddef.h>
+#include <stdint.h>
#include <algorithm>
#include <chrono>
-#include <cmath>
-#include <cstddef>
#include <limits>
#include <utility>
+#include "alsa_pool.h"
+#include "card_type.h"
#include "decibel.h"
+#include "defs.h"
+#include "filter.h"
#include "flags.h"
+#include "input_mapping.h"
+#include "resampling_queue.h"
#include "shared/metrics.h"
+#include "shared/shared_defs.h"
#include "state.pb.h"
-#include "shared/timebase.h"
+#include "stereocompressor.h"
using namespace bmusb;
using namespace std;
} // namespace
-AudioMixer::AudioMixer(unsigned num_capture_cards, unsigned num_ffmpeg_inputs)
- : num_capture_cards(num_capture_cards),
- num_ffmpeg_inputs(num_ffmpeg_inputs),
- ffmpeg_inputs(new AudioDevice[num_ffmpeg_inputs]),
- limiter(OUTPUT_FREQUENCY),
+AudioMixer::AudioMixer()
+ : limiter(OUTPUT_FREQUENCY),
correlation(OUTPUT_FREQUENCY)
{
for (unsigned bus_index = 0; bus_index < MAX_BUSES; ++bus_index) {
device->resampling_queue.reset();
} else {
device->resampling_queue.reset(new ResamplingQueue(
- device_spec, device->capture_frequency, OUTPUT_FREQUENCY, device->interesting_channels.size(),
+ spec_to_string(device_spec), device->capture_frequency, OUTPUT_FREQUENCY, device->interesting_channels.size(),
global_flags.audio_queue_length_ms * 0.001));
}
}
}
unsigned num_channels = device->interesting_channels.size();
- assert(num_channels > 0);
+ if (num_channels == 0) {
+ // No buses use this device; throw it away. (Normally, we should not
+ // be here, but probably, we are in the process of changing a mapping,
+ // and the queue just isn't gone yet. In any case, returning is harmless.)
+ return true;
+ }
// Convert the audio to fp32.
unique_ptr<float[]> audio(new float[num_samples * num_channels]);
return &video_cards[device.index];
case InputSourceType::ALSA_INPUT:
return &alsa_inputs[device.index];
- case InputSourceType::FFMPEG_VIDEO_INPUT:
- return &ffmpeg_inputs[device.index];
case InputSourceType::SILENCE:
default:
assert(false);
memset(output, 0, num_samples * 2 * sizeof(*output));
} else {
assert(bus.device.type == InputSourceType::CAPTURE_CARD ||
- bus.device.type == InputSourceType::ALSA_INPUT ||
- bus.device.type == InputSourceType::FFMPEG_VIDEO_INPUT);
+ bus.device.type == InputSourceType::ALSA_INPUT);
const float *lsrc, *rsrc;
unsigned lstride, rstride;
float *dptr = output;
ret.push_back(device_spec);
}
}
- for (unsigned card_index = 0; card_index < num_ffmpeg_inputs; ++card_index) {
- const DeviceSpec device_spec{InputSourceType::FFMPEG_VIDEO_INPUT, card_index};
- if (!find_audio_device(device_spec)->interesting_channels.empty()) {
- ret.push_back(device_spec);
- }
- }
return ret;
}
lock_guard<timed_mutex> lock(audio_mutex);
map<DeviceSpec, DeviceInfo> devices;
- for (unsigned card_index = 0; card_index < num_capture_cards; ++card_index) {
+ for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) {
const DeviceSpec spec{ InputSourceType::CAPTURE_CARD, card_index };
const AudioDevice *device = &video_cards[card_index];
DeviceInfo info;
info.display_name = device->display_name;
- info.num_channels = 8;
+ info.num_channels = device->num_channels;
devices.insert(make_pair(spec, info));
}
vector<ALSAPool::Device> available_alsa_devices = alsa_pool.get_devices();
info.alsa_address = device.address;
devices.insert(make_pair(spec, info));
}
- for (unsigned card_index = 0; card_index < num_ffmpeg_inputs; ++card_index) {
- const DeviceSpec spec{ InputSourceType::FFMPEG_VIDEO_INPUT, card_index };
- const AudioDevice *device = &ffmpeg_inputs[card_index];
- DeviceInfo info;
- info.display_name = device->display_name;
- info.num_channels = 2;
- devices.insert(make_pair(spec, info));
- }
return devices;
}
-void AudioMixer::set_display_name(DeviceSpec device_spec, const string &name)
+void AudioMixer::set_device_parameters(DeviceSpec device_spec, const std::string &display_name, CardType card_type, unsigned num_channels, bool active)
{
AudioDevice *device = find_audio_device(device_spec);
lock_guard<timed_mutex> lock(audio_mutex);
- device->display_name = name;
+ if (active || device->display_name.empty()) {
+ device->display_name = display_name;
+ }
+ device->card_type = card_type;
+ device->active = active;
+}
+
+bool AudioMixer::get_active(DeviceSpec device_spec)
+{
+ AudioDevice *device = find_audio_device(device_spec);
+
+ lock_guard<timed_mutex> lock(audio_mutex);
+ return device->active;
}
void AudioMixer::serialize_device(DeviceSpec device_spec, DeviceSpecProto *device_spec_proto)
case InputSourceType::ALSA_INPUT:
alsa_pool.serialize_device(device_spec.index, device_spec_proto);
break;
- case InputSourceType::FFMPEG_VIDEO_INPUT:
- device_spec_proto->set_type(DeviceSpecProto::FFMPEG_VIDEO_INPUT);
- device_spec_proto->set_index(device_spec.index);
- device_spec_proto->set_display_name(ffmpeg_inputs[device_spec.index].display_name);
- break;
}
}
void AudioMixer::set_simple_input(unsigned card_index)
{
- assert(card_index < num_capture_cards + num_ffmpeg_inputs);
+ assert(card_index < MAX_VIDEO_CARDS);
InputMapping new_input_mapping;
InputMapping::Bus input;
input.name = "Main";
- if (card_index >= num_capture_cards) {
- input.device = DeviceSpec{InputSourceType::FFMPEG_VIDEO_INPUT, card_index - num_capture_cards};
- } else {
- input.device = DeviceSpec{InputSourceType::CAPTURE_CARD, card_index};
- }
+ input.device = DeviceSpec{InputSourceType::CAPTURE_CARD, card_index};
input.source_channel[0] = 0;
input.source_channel[1] = 1;
input_mapping.buses[0].source_channel[0] == 0 &&
input_mapping.buses[0].source_channel[1] == 1) {
return input_mapping.buses[0].device.index;
- } else if (input_mapping.buses.size() == 1 &&
- input_mapping.buses[0].device.type == InputSourceType::FFMPEG_VIDEO_INPUT &&
- input_mapping.buses[0].source_channel[0] == 0 &&
- input_mapping.buses[0].source_channel[1] == 1) {
- return input_mapping.buses[0].device.index + num_capture_cards;
} else {
return numeric_limits<unsigned>::max();
}
map<DeviceSpec, set<unsigned>> interesting_channels;
for (const InputMapping::Bus &bus : new_input_mapping.buses) {
if (bus.device.type == InputSourceType::CAPTURE_CARD ||
- bus.device.type == InputSourceType::ALSA_INPUT ||
- bus.device.type == InputSourceType::FFMPEG_VIDEO_INPUT) {
+ 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]);
if (bus.device.type == InputSourceType::SILENCE) {
metrics.labels.emplace_back("source_type", "silence");
} else if (bus.device.type == InputSourceType::CAPTURE_CARD) {
- metrics.labels.emplace_back("source_type", "capture_card");
+ AudioDevice *device = find_audio_device(bus.device);
+ if (device->card_type == CardType::FFMPEG_INPUT) {
+ metrics.labels.emplace_back("source_type", "ffmpeg_video_input");
+ } else {
+ metrics.labels.emplace_back("source_type", "capture_card");
+ }
} else if (bus.device.type == InputSourceType::ALSA_INPUT) {
metrics.labels.emplace_back("source_type", "alsa_input");
- } else if (bus.device.type == InputSourceType::FFMPEG_VIDEO_INPUT) {
- metrics.labels.emplace_back("source_type", "ffmpeg_video_input");
} else {
assert(false);
}
reset_resampler_mutex_held(device_spec);
}
}
- for (unsigned card_index = 0; card_index < num_ffmpeg_inputs; ++card_index) {
- const DeviceSpec device_spec{InputSourceType::FFMPEG_VIDEO_INPUT, card_index};
- AudioDevice *device = find_audio_device(device_spec);
- if (device->interesting_channels != interesting_channels[device_spec]) {
- device->interesting_channels = interesting_channels[device_spec];
- reset_resampler_mutex_held(device_spec);
- }
- }
input_mapping = new_input_mapping;
}
return true;
} else {
assert(bus.device.type == InputSourceType::CAPTURE_CARD ||
- bus.device.type == InputSourceType::ALSA_INPUT ||
- bus.device.type == InputSourceType::FFMPEG_VIDEO_INPUT);
+ bus.device.type == InputSourceType::ALSA_INPUT);
return bus.source_channel[0] == bus.source_channel[1];
}
}
+// This is perhaps not the most user-friendly output, but it's at least better
+// than the raw index. It would be nice to have it identical to
+// Mixer::description_for_card for capture cards, though.
+string AudioMixer::spec_to_string(DeviceSpec device_spec) const
+{
+ char buf[256];
+
+ switch (device_spec.type) {
+ case InputSourceType::SILENCE:
+ return "<silence>";
+ case InputSourceType::CAPTURE_CARD: {
+ const AudioDevice *device = find_audio_device(device_spec);
+ if (device->card_type == CardType::FFMPEG_INPUT) {
+ snprintf(buf, sizeof(buf), "Virtual capture card %u (%s)", device_spec.index, device->display_name.c_str());
+ } else {
+ snprintf(buf, sizeof(buf), "Capture card %u (%s)", device_spec.index, device->display_name.c_str());
+ }
+ return buf;
+ }
+ case InputSourceType::ALSA_INPUT:
+ snprintf(buf, sizeof(buf), "ALSA input %u", device_spec.index);
+ return buf;
+ default:
+ assert(false);
+ }
+}
+
+
AudioMixer *global_audio_mixer = nullptr;