2019-10-25 19:51:40 -04:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <mutex>
|
|
|
|
#include <deque>
|
|
|
|
#include <memory>
|
|
|
|
#include <iostream>
|
|
|
|
#include <functional>
|
2021-03-27 16:32:18 -04:00
|
|
|
#include <cassert>
|
2020-02-08 10:50:48 -05:00
|
|
|
#include "./AudioSamples.h"
|
|
|
|
#include "./driver/AudioDriver.h"
|
|
|
|
#include "../ring_buffer.h"
|
2019-10-25 19:51:40 -04:00
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
#define ssize_t int64_t
|
|
|
|
#endif
|
|
|
|
|
2020-02-08 10:50:48 -05:00
|
|
|
namespace tc::audio {
|
2019-10-25 19:51:40 -04:00
|
|
|
class AudioOutput;
|
2020-02-09 08:53:39 -05:00
|
|
|
class AudioResampler;
|
2021-03-27 16:32:18 -04:00
|
|
|
class AudioProcessor;
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-25 10:21:47 -04:00
|
|
|
enum struct OverflowStrategy {
|
|
|
|
ignore,
|
|
|
|
discard_buffer_all,
|
|
|
|
discard_buffer_half,
|
|
|
|
discard_input
|
|
|
|
};
|
2021-03-21 17:39:10 -04:00
|
|
|
|
2019-10-25 19:51:40 -04:00
|
|
|
class AudioOutputSource {
|
|
|
|
friend class AudioOutput;
|
|
|
|
public:
|
2021-03-25 10:21:47 -04:00
|
|
|
enum struct BufferState {
|
|
|
|
/* Awaiting enough samples to replay and apply the fadein effect */
|
|
|
|
buffering,
|
|
|
|
/* We have encountered a buffer underflow. Applying fadeout effect and changing state to buffering. */
|
|
|
|
fadeout,
|
|
|
|
/* We're just normally replaying audio */
|
|
|
|
playing
|
|
|
|
};
|
|
|
|
|
|
|
|
[[nodiscard]] inline auto channel_count() const -> size_t { return this->channel_count_; }
|
|
|
|
[[nodiscard]] inline auto sample_rate() const -> size_t { return this->sample_rate_; }
|
|
|
|
[[nodiscard]] inline auto state() const -> BufferState { return this->buffer_state; }
|
2021-03-21 17:39:10 -04:00
|
|
|
|
2021-03-25 10:21:47 -04:00
|
|
|
/**
|
|
|
|
* The maximum amount of samples which could be buffered.
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline auto max_supported_buffering() const -> size_t {
|
|
|
|
return this->buffer.capacity() / this->channel_count_ / sizeof(float);
|
2020-02-08 10:50:48 -05:00
|
|
|
}
|
|
|
|
|
2021-03-25 10:21:47 -04:00
|
|
|
[[nodiscard]] inline auto max_buffering() const -> size_t {
|
2021-03-21 17:39:10 -04:00
|
|
|
const auto max_samples = this->max_supported_buffering();
|
|
|
|
if(this->max_buffered_samples_ && this->max_buffered_samples_ <= max_samples) {
|
|
|
|
return this->max_buffered_samples_;
|
|
|
|
}
|
2020-02-08 10:50:48 -05:00
|
|
|
|
|
|
|
return max_samples;
|
|
|
|
}
|
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
/**
|
|
|
|
* Sample count which still need to be replayed before newly emplaced buffers will be played.
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
[[nodiscard]] inline size_t currently_buffered_samples() const {
|
2021-03-25 10:21:47 -04:00
|
|
|
return this->buffer.fill_count() / this->channel_count_ / sizeof(float);
|
2020-03-18 18:32:57 -04:00
|
|
|
}
|
|
|
|
|
2020-05-01 14:03:55 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
[[nodiscard]] inline size_t min_buffered_samples() const { return this->min_buffered_samples_; }
|
|
|
|
[[nodiscard]] inline size_t max_buffered_samples() const { return this->max_buffered_samples_; }
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
bool set_min_buffered_samples(size_t /* target samples */);
|
|
|
|
bool set_max_buffered_samples(size_t /* target samples */);
|
|
|
|
|
2021-03-25 10:21:47 -04:00
|
|
|
OverflowStrategy overflow_strategy{OverflowStrategy::discard_buffer_half};
|
2019-10-25 19:51:40 -04:00
|
|
|
|
|
|
|
/* if it returns true then the it means that the buffer has been refilled, we have to test again */
|
2020-02-09 08:53:39 -05:00
|
|
|
std::function<bool(size_t /* sample count */)> on_underflow;
|
2019-10-25 19:51:40 -04:00
|
|
|
std::function<void(size_t /* sample count */)> on_overflow;
|
|
|
|
std::function<void()> on_read; /* will be invoked after sample read, e.g. for buffer fullup */
|
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
void clear();
|
2019-10-25 19:51:40 -04:00
|
|
|
ssize_t enqueue_samples(const void * /* input buffer */, size_t /* sample count */);
|
2020-02-08 10:50:48 -05:00
|
|
|
ssize_t enqueue_samples_no_interleave(const void * /* input buffer */, size_t /* sample count */);
|
2021-03-21 17:39:10 -04:00
|
|
|
|
|
|
|
/* Consume N samples */
|
|
|
|
bool pop_samples(void* /* output buffer */, size_t /* sample count */);
|
2019-10-25 19:51:40 -04:00
|
|
|
private:
|
2021-03-21 17:39:10 -04:00
|
|
|
AudioOutputSource(size_t channel_count, size_t sample_rate, ssize_t max_buffer_sample_count = -1) :
|
2021-03-25 10:21:47 -04:00
|
|
|
channel_count_{channel_count}, sample_rate_{sample_rate},
|
|
|
|
buffer{max_buffer_sample_count == -1 ? channel_count * sample_rate * sizeof(float) : max_buffer_sample_count * channel_count * sizeof(float)}
|
2021-03-21 17:39:10 -04:00
|
|
|
{
|
2020-05-01 14:03:55 -04:00
|
|
|
this->clear();
|
2021-03-27 16:32:18 -04:00
|
|
|
|
|
|
|
this->fadein_frame_samples_ = sample_rate * 0.02;
|
|
|
|
this->fadeout_frame_samples_ = sample_rate * 0.016;
|
2020-02-08 10:50:48 -05:00
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-25 10:21:47 -04:00
|
|
|
size_t const channel_count_;
|
|
|
|
size_t const sample_rate_;
|
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
std::recursive_mutex buffer_mutex{};
|
2021-03-25 10:21:47 -04:00
|
|
|
BufferState buffer_state{BufferState::buffering};
|
2020-02-08 10:50:48 -05:00
|
|
|
tc::ring_buffer buffer;
|
2021-03-21 17:39:10 -04:00
|
|
|
|
|
|
|
size_t min_buffered_samples_{0};
|
|
|
|
size_t max_buffered_samples_{0};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fadeout and fadein properties.
|
|
|
|
* The fadeout sample count should always be lower than the fade in sample count.
|
|
|
|
*/
|
2021-03-27 16:32:18 -04:00
|
|
|
size_t fadein_frame_samples_;
|
|
|
|
size_t fadeout_frame_samples_;
|
2021-03-21 17:39:10 -04:00
|
|
|
size_t fadeout_samples_left{0};
|
|
|
|
|
|
|
|
/* Methods bellow do not acquire the buffer_mutex mutex */
|
|
|
|
ssize_t enqueue_samples_(const void * /* input buffer */, size_t /* sample count */);
|
|
|
|
bool pop_samples_(void* /* output buffer */, size_t /* sample count */);
|
|
|
|
|
|
|
|
void apply_fadeout();
|
|
|
|
void apply_fadein();
|
2019-10-25 19:51:40 -04:00
|
|
|
};
|
|
|
|
|
2020-02-08 10:50:48 -05:00
|
|
|
class AudioOutput : public AudioDevicePlayback::Source {
|
2019-10-25 19:51:40 -04:00
|
|
|
public:
|
|
|
|
AudioOutput(size_t /* channels */, size_t /* rate */);
|
|
|
|
virtual ~AudioOutput();
|
|
|
|
|
2020-02-08 10:50:48 -05:00
|
|
|
void set_device(const std::shared_ptr<AudioDevice>& /* device */);
|
|
|
|
bool playback(std::string& /* error */);
|
2019-10-25 19:51:40 -04:00
|
|
|
void close_device();
|
2020-02-08 10:50:48 -05:00
|
|
|
std::shared_ptr<AudioDevice> current_device() { return this->device; }
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
std::deque<std::weak_ptr<AudioOutputSource>> sources() {
|
|
|
|
std::lock_guard sources_lock{this->sources_mutex};
|
|
|
|
return this->sources_;
|
2019-10-25 19:51:40 -04:00
|
|
|
}
|
|
|
|
|
2020-03-18 18:32:57 -04:00
|
|
|
std::shared_ptr<AudioOutputSource> create_source(ssize_t /* buffer sample size */ = -1);
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
[[nodiscard]] inline size_t channel_count() const { return this->channel_count_; }
|
|
|
|
[[nodiscard]] inline size_t sample_rate() const { return this->sample_rate_; }
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
[[nodiscard]] inline float volume() const { return this->volume_modifier; }
|
|
|
|
inline void set_volume(float value) { this->volume_modifier = value; }
|
2021-03-27 16:32:18 -04:00
|
|
|
|
|
|
|
void register_audio_processor(const std::shared_ptr<AudioProcessor>&);
|
|
|
|
bool unregister_audio_processor(const std::shared_ptr<AudioProcessor>&);
|
2019-10-25 19:51:40 -04:00
|
|
|
private:
|
2021-03-27 16:32:18 -04:00
|
|
|
/* One audio chunk should be 10ms long */
|
|
|
|
constexpr static auto kChunkTimeMs{10};
|
|
|
|
|
2021-04-19 13:08:44 -04:00
|
|
|
void fill_buffer(void *, size_t request_sample_count, size_t sample_rate, size_t out_channels) override;
|
|
|
|
void fill_buffer_nochecks(void *, size_t request_sample_count, size_t out_channels);
|
2021-03-27 16:32:18 -04:00
|
|
|
void fill_chunk_buffer();
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
size_t const channel_count_;
|
|
|
|
size_t const sample_rate_;
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
std::mutex sources_mutex{};
|
|
|
|
std::deque<std::weak_ptr<AudioOutputSource>> sources_{};
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-27 16:32:18 -04:00
|
|
|
std::mutex processors_mutex{};
|
|
|
|
std::vector<std::shared_ptr<AudioProcessor>> audio_processors_{};
|
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
std::recursive_mutex device_lock{};
|
2020-02-08 10:50:48 -05:00
|
|
|
std::shared_ptr<AudioDevice> device{nullptr};
|
2021-03-21 17:39:10 -04:00
|
|
|
std::shared_ptr<AudioDevicePlayback> playback_{nullptr};
|
|
|
|
std::unique_ptr<AudioResampler> resampler_{nullptr};
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2020-02-09 08:53:39 -05:00
|
|
|
/* only access there buffers within the audio loop! */
|
2021-03-21 17:39:10 -04:00
|
|
|
void** source_merge_buffer{nullptr};
|
2021-03-27 16:32:18 -04:00
|
|
|
size_t source_merge_buffer_length{0};
|
2019-10-25 19:51:40 -04:00
|
|
|
|
2021-03-27 16:32:18 -04:00
|
|
|
/*
|
|
|
|
* The chunk buffer will be large enough to hold
|
|
|
|
* a chunk of pcm data with our current configuration.
|
|
|
|
*/
|
|
|
|
void* chunk_buffer{nullptr};
|
|
|
|
size_t chunk_buffer_length{0};
|
|
|
|
size_t chunk_buffer_sample_length{0};
|
|
|
|
size_t chunk_buffer_sample_offset{0};
|
|
|
|
|
2021-04-19 13:08:44 -04:00
|
|
|
size_t current_output_sample_rate{0};
|
2021-03-27 16:32:18 -04:00
|
|
|
size_t current_output_channels{0};
|
2020-02-09 08:53:39 -05:00
|
|
|
|
2021-03-27 16:32:18 -04:00
|
|
|
void ensure_chunk_buffer_space(size_t /* output samples */);
|
2019-10-25 19:51:40 -04:00
|
|
|
void cleanup_buffers();
|
|
|
|
|
2021-03-21 17:39:10 -04:00
|
|
|
float volume_modifier{1.f};
|
2021-03-27 16:32:18 -04:00
|
|
|
|
|
|
|
[[nodiscard]] inline auto chunk_local_sample_count() const {
|
|
|
|
assert(this->playback_);
|
|
|
|
return (this->sample_rate_ * kChunkTimeMs) / 1000;
|
|
|
|
}
|
2019-10-25 19:51:40 -04:00
|
|
|
};
|
2020-02-08 10:50:48 -05:00
|
|
|
}
|