283 lines
9.3 KiB
C++
Raw Normal View History

2019-10-26 01:51:40 +02:00
#include "AudioOutput.h"
#include "AudioMerger.h"
#include "../logger.h"
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
using namespace tc;
using namespace tc::audio;
void AudioOutputSource::clear() {
2020-02-08 16:50:48 +01:00
this->buffer.clear();
this->buffering = true;
2019-10-26 01:51:40 +02:00
}
ssize_t AudioOutputSource::pop_samples(void *buffer, size_t samples) {
2020-02-08 16:50:48 +01:00
size_t written{0}, written_bytes{0};
load_buffer:
auto available_bytes = this->buffer.fill_count();
if(available_bytes < sizeof(float) * this->channel_count) return written;
auto available_samples = available_bytes / sizeof(float) / this->channel_count;
//log_trace(category::audio, tr("Min: {}, Max: {}, Current: {}, Buffering: {}"), this->min_buffered_samples, this->max_buffered_samples, available_samples, this->buffering);
if(this->buffering && available_samples < this->min_buffered_samples) return -2;
this->buffering = false;
if(available_samples >= samples - written) {
const auto byte_length = (samples - written) * sizeof(float) * this->channel_count;
memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
this->buffer.advance_read_ptr(byte_length);
return samples;
} else {
const auto byte_length = available_samples * sizeof(float) * this->channel_count;
memcpy((char*) buffer + written_bytes, this->buffer.read_ptr(), byte_length);
this->buffer.advance_read_ptr(byte_length);
written += available_samples;
written_bytes += byte_length;
}
if(auto fn = this->on_underflow; fn)
if(fn())
goto load_buffer;
this->buffering = true;
2019-10-26 01:51:40 +02:00
if(this->on_read)
this->on_read();
2020-02-08 16:50:48 +01:00
return written; /* return the written samples */
2019-10-26 01:51:40 +02:00
}
ssize_t AudioOutputSource::enqueue_samples(const void *buffer, size_t samples) {
2020-02-08 16:50:48 +01:00
size_t enqueued{0};
auto free_bytes = this->buffer.free_count();
auto free_samples = free_bytes / sizeof(float) / this->channel_count;
if(this->max_buffered_samples && free_samples > this->max_buffered_samples) free_samples = this->max_buffered_samples;
if(free_samples >= samples) {
const auto byte_length = samples * sizeof(float) * this->channel_count;
memcpy(this->buffer.write_ptr(), buffer, byte_length);
this->buffer.advance_write_ptr(byte_length);
return samples;
} else {
const auto byte_length = free_samples * sizeof(float) * this->channel_count;
memcpy(this->buffer.write_ptr(), buffer, byte_length);
this->buffer.advance_write_ptr(byte_length);
enqueued += free_samples;
}
if(auto fn = this->on_overflow; fn)
fn(samples - enqueued);
switch (this->overflow_strategy) {
case overflow_strategy::discard_input:
return -2;
case overflow_strategy::discard_buffer_all:
this->buffer.clear();
break;
case overflow_strategy::discard_buffer_half:
this->buffer.advance_read_ptr(this->buffer.fill_count() / 2);
break;
case overflow_strategy::ignore:
break;
}
return enqueued;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
ssize_t AudioOutputSource::enqueue_samples_no_interleave(const void *buffer, size_t samples) {
auto free_bytes = this->buffer.free_count();
auto free_samples = free_bytes / sizeof(float) / this->channel_count;
if(this->max_buffered_samples && free_samples > this->max_buffered_samples) free_samples = this->max_buffered_samples;
auto samples_to_write{samples};
if(samples_to_write > free_samples) samples_to_write = free_samples;
const auto enqueued{samples_to_write};
{
auto src_buffer = (const float*) buffer;
auto target_buffer = (float*) this->buffer.write_ptr();
while (samples_to_write-- > 0) {
*target_buffer = *src_buffer;
*(target_buffer + 1) = *(src_buffer + samples);
target_buffer += 2;
src_buffer++;
}
}
if(auto fn = this->on_overflow; fn)
fn(samples - enqueued);
switch (this->overflow_strategy) {
case overflow_strategy::discard_input:
return -2;
case overflow_strategy::discard_buffer_all:
this->buffer.clear();
break;
case overflow_strategy::discard_buffer_half:
this->buffer.advance_read_ptr(this->buffer.fill_count() / 2);
break;
case overflow_strategy::ignore:
break;
}
return enqueued;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
AudioOutput::AudioOutput(size_t channels, size_t rate) : _channel_count(channels), _sample_rate(rate) { }
2019-10-26 01:51:40 +02:00
AudioOutput::~AudioOutput() {
this->close_device();
this->cleanup_buffers();
}
std::shared_ptr<AudioOutputSource> AudioOutput::create_source() {
auto result = shared_ptr<AudioOutputSource>(new AudioOutputSource(this, this->_channel_count, this->_sample_rate));
{
lock_guard lock(this->sources_lock);
this->_sources.push_back(result);
}
return result;
}
void AudioOutput::delete_source(const std::shared_ptr<tc::audio::AudioOutputSource> &source) {
{
lock_guard lock(this->sources_lock);
auto it = find(this->_sources.begin(), this->_sources.end(), source);
if(it != this->_sources.end())
this->_sources.erase(it);
}
source->handle = nullptr;
}
void AudioOutput::cleanup_buffers() {
lock_guard buffer_lock(this->buffer_lock);
if(this->source_buffer)
free(this->source_buffer);
if(this->source_merge_buffer)
free(this->source_merge_buffer);
this->source_merge_buffer = nullptr;
this->source_buffer = nullptr;
this->source_merge_buffer_length = 0;
this->source_buffer_length = 0;
}
2020-02-08 20:45:04 +01:00
void AudioOutput::fill_buffer(void *output, size_t frameCount, size_t channels) {
2019-10-26 01:51:40 +02:00
lock_guard buffer_lock(this->buffer_lock);
2020-02-08 16:50:48 +01:00
if(this->_volume <= 0) {
for(auto& source : this->_sources)
source->pop_samples(nullptr, frameCount);
memset(output, 0, sizeof(frameCount) * channels * sizeof(float));
return;
}
2019-10-26 01:51:40 +02:00
size_t buffer_length = frameCount * 4 * this->_channel_count;
size_t sources = 0;
size_t actual_sources = 0;
{
lock_guard lock(this->sources_lock);
sources = this->_sources.size();
actual_sources = sources;
if(sources > 0) {
2020-02-08 16:50:48 +01:00
/* allocate the required space */
auto source_buffer_length = buffer_length * sources;
auto source_merge_buffer_length = sizeof(void*) * sources;
//TODO: Move this out of the loop?
{
if(this->source_buffer_length < source_buffer_length || !this->source_buffer) {
if(this->source_buffer)
free(this->source_buffer);
this->source_buffer = malloc(source_buffer_length);
this->source_buffer_length = source_buffer_length;
}
if(this->source_merge_buffer_length < source_merge_buffer_length || !this->source_merge_buffer) {
if (this->source_merge_buffer)
free(this->source_merge_buffer);
this->source_merge_buffer = (void **) malloc(source_merge_buffer_length);
this->source_merge_buffer_length = source_merge_buffer_length;
}
}
2019-10-26 01:51:40 +02:00
for(size_t index = 0; index < sources; index++) {
auto& source = this->_sources[index];
2020-02-08 16:50:48 +01:00
this->source_merge_buffer[index] = (char*) this->source_buffer + (buffer_length * index);
auto written_frames = this->_sources[index]->pop_samples(this->source_merge_buffer[index], frameCount);
if(written_frames != frameCount) {
if(written_frames <= 0) {
this->source_merge_buffer[index] = nullptr;
actual_sources--;
} else {
/* fill up the rest with silence (0) */
auto written = written_frames * this->_channel_count * 4;
memset((char*) this->source_merge_buffer[index] + written, 0, (frameCount - written_frames) * this->_channel_count * 4);
}
}
2019-10-26 01:51:40 +02:00
}
}
}
2020-02-08 16:50:48 +01:00
if(actual_sources > 0) {
if(!merge::merge_n_sources(output, this->source_merge_buffer, sources, this->_channel_count, frameCount))
2019-10-26 01:51:40 +02:00
log_warn(category::audio, tr("failed to merge buffers!"));
2020-02-08 16:50:48 +01:00
auto volume = this->_volume;
if(volume != 1) {
auto float_length = this->_channel_count * frameCount;
auto data = (float*) output;
while(float_length-- > 0)
*data++ *= volume;
2019-10-26 01:51:40 +02:00
}
} else {
2020-02-08 16:50:48 +01:00
memset(output, 0, this->_channel_count * sizeof(float) * frameCount);
2019-10-26 01:51:40 +02:00
}
}
2020-02-08 16:50:48 +01:00
void AudioOutput::set_device(const std::shared_ptr<AudioDevice> &device) {
lock_guard lock(this->device_lock);
if(this->device == device) return;
2019-10-26 01:51:40 +02:00
2020-02-08 16:50:48 +01:00
this->close_device();
this->device = device;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
void AudioOutput::close_device() {
lock_guard lock(this->device_lock);
if(this->_playback) {
this->_playback->remove_source(this);
this->_playback->stop_if_possible();
this->_playback.reset();
}
this->device = nullptr;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
bool AudioOutput::playback(std::string& error) {
lock_guard lock(this->device_lock);
if(!this->device) {
error = "invalid device handle";
return false;
}
if(this->_playback) return true;
2019-10-26 01:51:40 +02:00
2020-02-08 16:50:48 +01:00
this->_playback = this->device->playback();
if(!this->_playback) {
error = "failed to allocate memory";
return false;
2019-10-26 01:51:40 +02:00
}
2020-02-08 16:50:48 +01:00
this->_playback->register_source(this);
return this->_playback->start(error);
2019-10-26 01:51:40 +02:00
}