#include #include #include #include "./AudioInput.h" #include "./AudioReframer.h" #include "./AudioResampler.h" #include "../logger.h" #include "AudioGain.h" using namespace std; using namespace tc; using namespace tc::audio; AudioConsumer::AudioConsumer(tc::audio::AudioInput *handle, size_t channel_count, size_t sample_rate, size_t frame_size) : handle(handle), channel_count(channel_count), sample_rate(sample_rate) , frame_size(frame_size) { if(this->frame_size > 0) { this->reframer = std::make_unique(channel_count, frame_size); this->reframer->on_frame = [&](const void* buffer) { this->handle_framed_data(buffer, this->frame_size); }; } } void AudioConsumer::handle_framed_data(const void *buffer, size_t samples) { std::unique_lock read_callback_lock(this->on_read_lock); auto function = this->on_read; /* copy */ read_callback_lock.unlock(); if(!function) return; function(buffer, samples); } void AudioConsumer::process_data(const void *buffer, size_t samples) { if(this->reframer) { this->reframer->process(buffer, samples); } else { this->handle_framed_data(buffer, samples); } } AudioInput::AudioInput(size_t channels, size_t rate) : _channel_count(channels), _sample_rate(rate) {} AudioInput::~AudioInput() { this->close_device(); { std::lock_guard lock(this->consumers_lock); for(const auto& consumer : this->_consumers) consumer->handle = nullptr; } free(this->resample_buffer); } void AudioInput::set_device(const std::shared_ptr &device) { std::lock_guard lock{this->input_source_lock}; if(device == this->input_device) return; this->close_device(); this->input_device = device; } void AudioInput::close_device() { std::lock_guard lock{this->input_source_lock}; if(this->input_recorder) { this->input_recorder->remove_consumer(this); this->input_recorder->stop_if_possible(); this->input_recorder.reset(); } this->_resampler = nullptr; this->input_device = nullptr; } bool AudioInput::record(std::string& error) { std::lock_guard lock{this->input_source_lock}; if(!this->input_device) { error = "no device"; return false; } if(this->input_recorder) { return true; } this->input_recorder = this->input_device->record(); if(!this->input_recorder) { error = "failed to get recorder"; return false; } if(this->input_recorder->sample_rate() != this->sample_rate()) { this->_resampler = std::make_unique(this->input_recorder->sample_rate(), this->sample_rate(), this->_channel_count); } this->input_recorder->register_consumer(this); if(!this->input_recorder->start(error)) { this->input_recorder->remove_consumer(this); this->input_recorder.reset(); return false; } return true; } bool AudioInput::recording() { return !!this->input_recorder; } void AudioInput::stop() { if(!this->input_recorder) return; this->input_recorder->remove_consumer(this); this->input_recorder->stop_if_possible(); this->input_recorder.reset(); } std::shared_ptr AudioInput::create_consumer(size_t frame_length) { auto result = std::shared_ptr(new AudioConsumer(this, this->_channel_count, this->_sample_rate, frame_length)); { std::lock_guard lock(this->consumers_lock); this->_consumers.push_back(result); } return result; } void AudioInput::delete_consumer(const std::shared_ptr &source) { { std::lock_guard lock(this->consumers_lock); auto it = find(this->_consumers.begin(), this->_consumers.end(), source); if(it != this->_consumers.end()) this->_consumers.erase(it); } source->handle = nullptr; } void AudioInput::consume(const void *input, size_t frameCount, size_t channels) { if(channels != this->_channel_count) { log_critical(category::audio, tr("Channel count miss match (input)! Fixme!")); return; } if(this->_resampler) { const auto expected_size = this->_resampler->estimated_output_size(frameCount); const auto expected_byte_size = expected_size * this->_channel_count * sizeof(float); if(this->resample_buffer_size < expected_byte_size) { free(this->resample_buffer); this->resample_buffer = malloc(expected_byte_size); this->resample_buffer_size = expected_byte_size; } auto result = this->_resampler->process(this->resample_buffer, input, frameCount); if(result < 0) { log_error(category::audio, tr("Failed to resample input audio: {}"), result); return; } frameCount = (size_t) result; input = this->resample_buffer; audio::apply_gain(this->resample_buffer, this->_channel_count, frameCount, this->_volume); } else if(this->_volume != 1) { const auto byte_size = frameCount * this->_channel_count * sizeof(float); if(this->resample_buffer_size < byte_size) { free(this->resample_buffer); this->resample_buffer = malloc(byte_size); this->resample_buffer_size = byte_size; } memcpy(this->resample_buffer, input, byte_size); input = this->resample_buffer; audio::apply_gain(this->resample_buffer, this->_channel_count, frameCount, this->_volume); } auto begin = std::chrono::system_clock::now(); for(const auto& consumer : this->consumers()) consumer->process_data(input, frameCount); auto end = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast(end - begin).count(); if(ms > 5) { log_warn(category::audio, tr("Processing of audio input needed {}ms. This could be an issue!"), std::chrono::duration_cast(end - begin).count()); } }