// // Created by wolverindev on 07.02.20. // #include #include #include #include "AudioDriver.h" #include "SoundIO.h" #include "../../logger.h" #include "../AudioMerger.h" using namespace tc::audio; namespace tc::audio { inline bool ends_with(std::string const & value, std::string const & ending) { if (ending.size() > value.size()) return false; return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } std::deque> devices() { std::deque> result{}; for(auto& backend : SoundIOBackendHandler::all_backends()) { auto input_devices = backend->input_devices(); auto output_devices = backend->output_devices(); result.insert(result.end(), input_devices.begin(), input_devices.end()); result.insert(result.end(), output_devices.begin(), output_devices.end()); } #ifdef WIN32 //Remove all not raw devices. We do not support shared mode yet result.erase(std::remove_if(result.begin(), result.end(), [](const std::shared_ptr& device) { if(device->driver() != "WASAPI") return false; return !ends_with(device->id(), "_raw"); }), result.end()); #endif return result; } std::shared_ptr find_device_by_id(const std::string_view& id, bool input) { for(auto& backend : SoundIOBackendHandler::all_backends()) { for(auto& dev : input ? backend->input_devices() : backend->output_devices()) if(dev->id() == id) return dev; } return nullptr; } std::mutex initialize_lock{}; std::deque initialize_callbacks{}; int initialize_state{0}; /* 0 := not initialized | 1 := initialized | 2 := initializing */ void _initialize() { SoundIOBackendHandler::initialize_all(); SoundIOBackendHandler::connect_all(); } void _finalize() { SoundIOBackendHandler::shutdown_all(); } void initialize(const initialize_callback_t& callback) { { std::unique_lock init_lock{initialize_lock}; if(initialize_state == 2) { if(callback) initialize_callbacks.push_back(callback); return; } else if(initialize_state == 1) { init_lock.unlock(); callback(); return; } else if(initialize_state != 0) { init_lock.unlock(); callback(); log_warn(category::audio, tr("Invalid initialize state ({})"), initialize_state); return; } initialize_state = 2; } std::thread init_thread([]{ _initialize(); std::unique_lock lock{initialize_lock}; auto callbacks = std::move(initialize_callbacks); initialize_state = 1; lock.unlock(); for(auto& callback : callbacks) callback(); }); threads::name(init_thread, tr("audio init")); init_thread.detach(); } void await_initialized() { std::condition_variable cv{}; std::mutex m{}; std::unique_lock init_lock{initialize_lock}; if(initialize_state != 2) return; initialize_callbacks.emplace_back([&]{ cv.notify_all(); }); init_lock.unlock(); std::unique_lock m_lock{m}; cv.wait(m_lock); } bool initialized() { std::unique_lock init_lock{initialize_lock}; return initialize_state == 1; } void finalize() { await_initialized(); _finalize(); } bool AudioDevicePlayback::start(std::string &error) { std::lock_guard lock{this->state_lock}; if(this->running) return true; if(!this->impl_start(error)) { log_error(category::audio, tr("Failed to start playback: {}"), error); return false; } this->running = true; return true; } void AudioDevicePlayback::stop_if_possible() { std::lock_guard lock{this->state_lock}; { std::lock_guard s_lock{this->source_lock}; if(!this->_sources.empty()) return; } this->impl_stop(); this->running = false; } void AudioDevicePlayback::stop() { std::lock_guard lock{this->state_lock}; if(this->running) return; this->impl_stop(); this->running = false; } void AudioDevicePlayback::register_source(Source* source) { std::lock_guard s_lock{this->source_lock}; this->_sources.push_back(source); } void AudioDevicePlayback::remove_source(Source* source) { std::lock_guard s_lock{this->source_lock}; auto index = find(this->_sources.begin(), this->_sources.end(), source); if(index == this->_sources.end()) return; this->_sources.erase(index); } #define TMP_BUFFER_SIZE 8096 void AudioDevicePlayback::fill_buffer(void *buffer, size_t samples, size_t channels) { std::lock_guard lock{this->source_lock}; const auto size = this->_sources.size(); if(size == 1) { this->_sources.front()->fill_buffer(buffer, samples, channels); } else if(size > 1) { this->_sources.front()->fill_buffer(buffer, samples, channels); uint8_t tmp_buffer[TMP_BUFFER_SIZE]; if(sizeof(float) * samples * channels > TMP_BUFFER_SIZE) { log_warn(category::audio, tr("Dropping input source data because of too small merge buffer")); return; } for(auto it = this->_sources.begin() + 1; it != this->_sources.end(); it++) { (*it)->fill_buffer(tmp_buffer, samples, channels); merge::merge_sources(buffer, buffer, tmp_buffer, channels, samples); } } else { memset(buffer, 0, samples * channels * sizeof(float)); } } bool AudioDeviceRecord::start(std::string &error) { std::lock_guard lock{this->state_lock}; if(this->running) return true; if(!this->impl_start(error)) { log_error(category::audio, tr("Failed to start record: {}"), error); return false; } this->running = true; return true; } void AudioDeviceRecord::stop_if_possible() { std::lock_guard lock{this->state_lock}; { std::lock_guard s_lock{this->consumer_lock}; if(!this->_consumers.empty()) return; } this->impl_stop(); this->running = false; } void AudioDeviceRecord::stop() { std::lock_guard lock{this->state_lock}; if(this->running) return; this->impl_stop(); this->running = false; } void AudioDeviceRecord::register_consumer(Consumer* source) { std::lock_guard s_lock{this->consumer_lock}; this->_consumers.push_back(source); } void AudioDeviceRecord::remove_consumer(Consumer* source) { std::lock_guard s_lock{this->consumer_lock}; auto index = find(this->_consumers.begin(), this->_consumers.end(), source); if(index == this->_consumers.end()) return; this->_consumers.erase(index); } }