X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=alsa_input.cpp;h=0f8e99c95b9b0d536451f26c85bc1fa3142a9003;hb=61116d03f84a53e96445c2b5d8686f2ea7e7c855;hp=cf9c69f4dd877213ba3d0230aba98fefdf7bbd62;hpb=ccb534bc43abdb3fb057e36bd832123934d84395;p=nageru diff --git a/alsa_input.cpp b/alsa_input.cpp index cf9c69f..0f8e99c 100644 --- a/alsa_input.cpp +++ b/alsa_input.cpp @@ -1,9 +1,11 @@ #include "alsa_input.h" #include "audio_mixer.h" #include "defs.h" +#include "state.pb.h" #include +#include #include #include @@ -450,19 +452,47 @@ ALSAPool::ProbeResult ALSAPool::probe_device_once(unsigned card_index, unsigned snd_ctl_card_info_alloca(&card_info); snd_ctl_card_info(ctl, card_info); - lock_guard lock(mu); - unsigned internal_dev_index = find_free_device_index(); - devices[internal_dev_index].address = address; - devices[internal_dev_index].name = snd_ctl_card_info_get_name(card_info); - devices[internal_dev_index].info = snd_pcm_info_get_name(pcm_info); - devices[internal_dev_index].num_channels = num_channels; - devices[internal_dev_index].state = Device::State::READY; + string name = snd_ctl_card_info_get_name(card_info); + string info = snd_pcm_info_get_name(pcm_info); + + unsigned internal_dev_index; + string display_name; + { + lock_guard lock(mu); + internal_dev_index = find_free_device_index(name, info, num_channels, address); + devices[internal_dev_index].address = address; + devices[internal_dev_index].name = name; + devices[internal_dev_index].info = info; + devices[internal_dev_index].num_channels = num_channels; + // Note: Purposefully does not overwrite held. + + display_name = devices[internal_dev_index].display_name(); + } fprintf(stderr, "%s: Probed successfully.\n", address); + reset_device(internal_dev_index); // Restarts it if it is held (ie., we just replaced a dead card). + + DeviceSpec spec{InputSourceType::ALSA_INPUT, internal_dev_index}; + global_audio_mixer->set_display_name(spec, display_name); + global_audio_mixer->trigger_state_changed_callback(); + return ALSAPool::ProbeResult::SUCCESS; } +void ALSAPool::unplug_device(unsigned card_index, unsigned dev_index) +{ + char address[256]; + snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index); + for (unsigned i = 0; i < devices.size(); ++i) { + if (devices[i].state != Device::State::EMPTY && + devices[i].state != Device::State::DEAD && + devices[i].address == address) { + free_card(i); + } + } +} + void ALSAPool::init() { thread(&ALSAPool::inotify_thread_func, this).detach(); @@ -511,7 +541,7 @@ void ALSAPool::inotify_thread_func() if (sscanf(event->name, "pcmC%uD%u%c", &card, &device, &type) == 3 && type == 'c') { if (event->mask & (IN_MOVED_FROM | IN_DELETE)) { printf("Deleted capture device: Card %u, device %u\n", card, device); - // TODO: Unplug. + unplug_device(card, device); } if (event->mask & (IN_MOVED_TO | IN_CREATE)) { printf("Adding capture device: Card %u, device %u\n", card, device); @@ -568,38 +598,111 @@ void ALSAPool::set_card_state(unsigned index, ALSAPool::Device::State state) bool silence = (state != ALSAPool::Device::State::RUNNING); while (!global_audio_mixer->silence_card(spec, silence)) ; + global_audio_mixer->trigger_state_changed_callback(); } -unsigned ALSAPool::find_free_device_index() +unsigned ALSAPool::find_free_device_index(const string &name, const string &info, unsigned num_channels, const string &address) { + // First try to find an exact match on a dead card. + for (unsigned i = 0; i < devices.size(); ++i) { + if (devices[i].state == Device::State::DEAD && + devices[i].address == address && + devices[i].name == name && + devices[i].info == info && + devices[i].num_channels == num_channels) { + devices[i].state = Device::State::READY; + return i; + } + } + + // Then try to find a match on everything but the address + // (probably that devices were plugged back in a different order). + // If we have two cards that are equal, this might get them mixed up, + // but we don't have anything better. + for (unsigned i = 0; i < devices.size(); ++i) { + if (devices[i].state == Device::State::DEAD && + devices[i].name == name && + devices[i].info == info && + devices[i].num_channels == num_channels) { + devices[i].state = Device::State::READY; + return i; + } + } + + // OK, so we didn't find a match; see if there are any empty slots. for (unsigned i = 0; i < devices.size(); ++i) { if (devices[i].state == Device::State::EMPTY) { devices[i].state = Device::State::READY; + devices[i].held = false; return i; } } + + // Failing that, we just insert the new device at the end. Device new_dev; new_dev.state = Device::State::READY; + new_dev.held = false; devices.push_back(new_dev); inputs.emplace_back(nullptr); return devices.size() - 1; } +unsigned ALSAPool::create_dead_card(const string &name, const string &info, unsigned num_channels) +{ + lock_guard lock(mu); + + // See if there are any empty slots. If not, insert one at the end. + vector::iterator free_device = + find_if(devices.begin(), devices.end(), + [](const Device &device) { return device.state == Device::State::EMPTY; }); + if (free_device == devices.end()) { + devices.push_back(Device()); + inputs.emplace_back(nullptr); + free_device = devices.end() - 1; + } + + free_device->state = Device::State::DEAD; + free_device->name = name; + free_device->info = info; + free_device->num_channels = num_channels; + free_device->held = true; + + return distance(devices.begin(), free_device); +} + +void ALSAPool::serialize_device(unsigned index, DeviceSpecProto *serialized) +{ + lock_guard lock(mu); + assert(index < devices.size()); + assert(devices[index].held); + serialized->set_type(DeviceSpecProto::ALSA_INPUT); + serialized->set_index(index); + serialized->set_display_name(devices[index].display_name()); + serialized->set_alsa_name(devices[index].name); + serialized->set_alsa_info(devices[index].info); + serialized->set_num_channels(devices[index].num_channels); + serialized->set_address(devices[index].address); +} + void ALSAPool::free_card(unsigned index) { DeviceSpec spec{InputSourceType::ALSA_INPUT, index}; while (!global_audio_mixer->silence_card(spec, true)) ; - lock_guard lock(mu); - if (devices[index].held) { - devices[index].state = Device::State::DEAD; - } else { - devices[index].state = Device::State::EMPTY; - inputs[index].reset(); - } - while (!devices.empty() && devices.back().state == Device::State::EMPTY) { - devices.pop_back(); - inputs.pop_back(); + { + lock_guard lock(mu); + if (devices[index].held) { + devices[index].state = Device::State::DEAD; + } else { + devices[index].state = Device::State::EMPTY; + inputs[index].reset(); + } + while (!devices.empty() && devices.back().state == Device::State::EMPTY) { + devices.pop_back(); + inputs.pop_back(); + } } + + global_audio_mixer->trigger_state_changed_callback(); }