TeaSpeak-Client/native/serverconnection/src/audio/AudioInput.cpp

193 lines
6.1 KiB
C++

#include <cstring>
#include <string>
#include "./AudioInput.h"
#include "./AudioReframer.h"
#include "./AudioResampler.h"
#include "./AudioMerger.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<InputReframer>(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<AudioDevice> &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<AudioResampler>(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<AudioConsumer> AudioInput::create_consumer(size_t frame_length) {
auto result = std::shared_ptr<AudioConsumer>(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<AudioConsumer> &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) {
if(channels < 1 || channels > 2) {
log_critical(category::audio, tr("Channel count miss match (Received: {})!"), channels);
}
this->ensure_resample_buffer_capacity(frameCount * this->_channel_count * sizeof(float));
audio::merge::merge_channels_interleaved(this->resample_buffer, this->_channel_count, input, channels, frameCount);
input = this->resample_buffer;
}
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);
this->ensure_resample_buffer_capacity(expected_byte_size);
size_t sample_count{expected_size};
if(!this->_resampler->process(this->resample_buffer, input, frameCount, sample_count)) {
log_error(category::audio, tr("Failed to resample input audio."));
return;
}
frameCount = sample_count;
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);
this->ensure_resample_buffer_capacity(byte_size);
if(this->resample_buffer != input) {
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<std::chrono::milliseconds>(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<chrono::milliseconds>(end - begin).count());
}
}
void AudioInput::ensure_resample_buffer_capacity(size_t size) {
if(this->resample_buffer_size < size) {
free(this->resample_buffer);
this->resample_buffer = malloc(size);
this->resample_buffer_size = size;
}
}