Revered the audio system back to PortAudio

This commit is contained in:
WolverinDEV
2020-02-13 22:16:34 +01:00
parent 91982ecc7b
commit 19b7e46137
21 changed files with 778 additions and 184 deletions
@@ -111,6 +111,8 @@ ssize_t AudioOutputSource::enqueue_samples_no_interleave(const void *buffer, siz
src_buffer++;
}
}
this->buffer.advance_write_ptr(enqueued * this->channel_count * sizeof(float));
if(enqueued == samples) return enqueued;
if(auto fn = this->on_overflow; fn)
fn(samples - enqueued);
@@ -173,17 +175,17 @@ void AudioOutput::cleanup_buffers() {
this->resample_overhead_samples = 0;
}
void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t channels) {
if(channels != this->_channel_count) {
log_critical(category::audio, tr("Channel count miss match (output)! Fixme!"));
void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t out_channels) {
if(out_channels != this->_channel_count) {
log_critical(category::audio, tr("Channel count miss match (output)! Expected: {} Received: {}. Fixme!"), this->_channel_count, out_channels);
return;
}
const auto local_frame_count = this->_resampler ? this->_resampler->input_size(out_frame_count) : out_frame_count;
auto local_frame_count = this->_resampler ? this->_resampler->input_size(out_frame_count) : out_frame_count;
void* const original_output{output};
if(this->resample_overhead_samples > 0) {
const auto samples_to_write = this->resample_overhead_samples > out_frame_count ? out_frame_count : this->resample_overhead_samples;
const auto byte_length = samples_to_write * sizeof(float) * channels;
const auto byte_length = samples_to_write * sizeof(float) * out_channels;
if(output) memcpy(output, this->resample_overhead_buffer, byte_length);
if(samples_to_write == out_frame_count) {
@@ -194,6 +196,7 @@ void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t chann
this->resample_overhead_samples = 0;
output = (char*) output + byte_length;
out_frame_count -= samples_to_write;
local_frame_count -= this->_resampler ? this->_resampler->input_size(samples_to_write) : samples_to_write;
}
}
@@ -204,7 +207,7 @@ void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t chann
} else if(this->_volume <= 0) {
for(auto& source : this->_sources)
source->pop_samples(nullptr, local_frame_count);
memset(output, 0, local_frame_count * channels * sizeof(float));
memset(output, 0, local_frame_count * out_channels * sizeof(float));
return;
}
@@ -272,6 +275,7 @@ void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t chann
if(resampled_samples != out_frame_count) {
if(resampled_samples > out_frame_count) {
const auto diff_length = resampled_samples - out_frame_count;
log_warn(category::audio, tr("enqueuing {} samples"), diff_length);
const auto overhead_buffer_offset = this->resample_overhead_samples * sizeof(float) * this->_channel_count;
const auto diff_byte_length = diff_length * sizeof(float) * this->_channel_count;
@@ -290,7 +294,7 @@ void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t chann
);
this->resample_overhead_samples += diff_length;
} else {
log_warn(category::audio, tr("Resampled samples does not match requested sampeles: {} <> {}"), resampled_samples, out_frame_count);
log_warn(category::audio, tr("Resampled samples does not match requested sampeles: {} <> {}. Sampled from {} to {}"), resampled_samples, out_frame_count, this->_resampler->input_rate(), this->_resampler->output_rate());
}
}
memcpy(output, this->source_buffer, out_frame_count * sizeof(float) * this->_channel_count);
@@ -308,6 +312,7 @@ void AudioOutput::fill_buffer(void *output, size_t out_frame_count, size_t chann
} else {
clear_buffer_exit:
memset(output, 0, this->_channel_count * sizeof(float) * out_frame_count);
return;
}
}
@@ -91,7 +91,7 @@ namespace tc::audio {
inline float volume() { return this->_volume; }
inline void set_volume(float value) { this->_volume = value; }
private:
void fill_buffer(void *, size_t out_frame_count, size_t channels) override;
void fill_buffer(void *, size_t out_frame_count, size_t out_channels) override;
size_t const _channel_count;
size_t const _sample_rate;
@@ -10,8 +10,7 @@
#define ssize_t int64_t
#endif
namespace tc {
namespace audio {
namespace tc::audio {
class AudioResampler {
public:
AudioResampler(size_t /* input rate */, size_t /* output rate */, size_t /* channels */);
@@ -23,10 +22,11 @@ namespace tc {
[[nodiscard]] inline long double io_ratio() { return (long double) this->_output_rate / (long double) this->_input_rate; }
[[nodiscard]] inline size_t estimated_output_size(size_t input_length) {
return (size_t) lroundl(this->io_ratio() * input_length) + 1;
if(!this->soxr_handle) return input_length; /* no resembling needed */
return (size_t) ceill(this->io_ratio() * input_length + *soxr_num_clips(this->soxr_handle)) + 1;
}
[[nodiscard]] inline size_t input_size(size_t output_length) {
return (size_t) lroundl((long double) this->_input_rate / (long double) this->_output_rate * output_length);
return (size_t) ceill((long double) this->_input_rate / (long double) this->_output_rate * output_length);
}
[[nodiscard]] inline bool valid() { return this->io_ratio() == 1 || this->soxr_handle != nullptr; }
@@ -39,5 +39,4 @@ namespace tc {
soxr_t soxr_handle = nullptr;
};
}
}
}
@@ -5,21 +5,22 @@
#include <thread>
#include <condition_variable>
#include <ThreadPool/ThreadHelper.h>
#include "AudioDriver.h"
#include "SoundIO.h"
#include "./AudioDriver.h"
#include "../../logger.h"
#include "../AudioMerger.h"
#ifdef HAVE_SOUNDIO
#include "./SoundIO.h"
#else
#include "PortAudio.h"
#endif
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<std::shared_ptr<AudioDevice>> devices() {
std::deque<std::shared_ptr<AudioDevice>> result{};
#ifdef HAVE_SOUNDIO
for(auto& backend : SoundIOBackendHandler::all_backends()) {
auto input_devices = backend->input_devices();
auto output_devices = backend->output_devices();
@@ -27,16 +28,26 @@ namespace tc::audio {
result.insert(result.end(), input_devices.begin(), input_devices.end());
result.insert(result.end(), output_devices.begin(), output_devices.end());
}
#else
auto devices = pa::devices();
result.insert(result.end(), devices.begin(), devices.end());
#endif
return result;
}
std::shared_ptr<AudioDevice> find_device_by_id(const std::string_view& id, bool input) {
#ifdef HAVE_SOUNDIO
for(auto& backend : SoundIOBackendHandler::all_backends()) {
for(auto& dev : input ? backend->input_devices() : backend->output_devices())
if(dev->id() == id)
return dev;
}
#else
for(auto& device : devices())
if(device->id() == id && (input ? device->is_input_supported() : device->is_output_supported()))
return device;
#endif
return nullptr;
}
@@ -44,6 +55,7 @@ namespace tc::audio {
std::deque<initialize_callback_t> initialize_callbacks{};
int initialize_state{0}; /* 0 := not initialized | 1 := initialized | 2 := initializing */
#ifdef HAVE_SOUNDIO
void _initialize() {
SoundIOBackendHandler::initialize_all();
SoundIOBackendHandler::connect_all();
@@ -52,6 +64,15 @@ namespace tc::audio {
void _finalize() {
SoundIOBackendHandler::shutdown_all();
}
#else
void _initialize() {
pa::initialize();
}
void _finalize() {
pa::finalize();
}
#endif
void initialize(const initialize_callback_t& callback) {
{
@@ -0,0 +1,111 @@
//
// Created by WolverinDEV on 13/02/2020.
//
#include "PortAudio.h"
#include "../../logger.h"
#include <portaudio.h>
namespace tc::audio::pa {
std::mutex _audio_devices_lock;
std::unique_ptr<std::deque<std::shared_ptr<PaAudioDevice>>> _audio_devices{};
void initialize_devices() {
std::lock_guard dev_lock{_audio_devices_lock};
_audio_devices = std::make_unique<std::deque<std::shared_ptr<PaAudioDevice>>>();
/* query devices */
auto device_count = Pa_GetDeviceCount();
if(device_count < 0) {
log_error(category::audio, tr("Pa_GetDeviceCount() returned {}"), device_count);
return;
}
for(PaDeviceIndex device_index = 0; device_index < device_count; device_index++) {
auto device_info = Pa_GetDeviceInfo(device_index);
if(!device_info) {
log_warn(category::audio, tr("Pa_GetDeviceInfo(...) failed for device {}"), device_index);
continue;
}
auto device_host_info = Pa_GetHostApiInfo(device_info->hostApi);
if(!device_host_info) {
log_warn(category::audio, tr("Pa_GetHostApiInfo(...) failed for device {} with host api {}"), device_index, device_info->hostApi);
continue;
}
_audio_devices->push_back(std::make_shared<PaAudioDevice>(device_index, device_info, device_host_info));
}
}
void initialize() {
Pa_Initialize();
initialize_devices();
}
void finalize() {
Pa_Terminate();
}
std::deque<std::shared_ptr<PaAudioDevice>> devices() {
std::lock_guard dev_lock{_audio_devices_lock};
return *_audio_devices;
}
/* device class */
PaAudioDevice::PaAudioDevice(PaDeviceIndex index, const PaDeviceInfo* info, const PaHostApiInfo* host)
: _index{index}, _info{info}, _host_info{host} { }
std::string PaAudioDevice::id() const {
return std::string{this->_info->name} + "_" + this->_host_info->name;
}
std::string PaAudioDevice::name() const {
return this->_info->name;
}
std::string PaAudioDevice::driver() const {
return this->_host_info->name;
}
bool PaAudioDevice::is_input_supported() const {
return this->_info->maxInputChannels > 0;
}
bool PaAudioDevice::is_output_supported() const {
return this->_info->maxOutputChannels > 0;
}
bool PaAudioDevice::is_input_default() const {
return this->_index == Pa_GetDefaultInputDevice();
}
bool PaAudioDevice::is_output_default() const {
return this->_index == Pa_GetDefaultOutputDevice();
}
std::shared_ptr<AudioDevicePlayback> PaAudioDevice::playback() {
if(!this->is_output_supported()) {
log_warn(category::audio, tr("Tried to create playback manager for device which does not supports it."));
return nullptr;
}
std::lock_guard lock{this->io_lock};
if(!this->_playback)
this->_playback = std::make_shared<PortAudioPlayback>(this->_index, this->_info);
return this->_playback;
}
std::shared_ptr<AudioDeviceRecord> PaAudioDevice::record() {
if(!this->is_input_supported()) {
log_warn(category::audio, tr("Tried to create record manager for device which does not supports it."));
return nullptr;
}
std::lock_guard lock{this->io_lock};
if(!this->_record)
this->_record = std::make_shared<PortAudioRecord>(this->_index, this->_info);
return this->_record;
}
}
@@ -0,0 +1,87 @@
#pragma once
#include <memory>
#include <array>
#include <vector>
#include <mutex>
#include <portaudio.h>
#include "./AudioDriver.h"
namespace tc::audio::pa {
class PortAudioPlayback : public AudioDevicePlayback {
public:
static constexpr auto kChannelCount{2};
static constexpr auto kSampleRate{44100};
static constexpr auto kTimeSpan{0.01};
explicit PortAudioPlayback(PaDeviceIndex index, const PaDeviceInfo* info);
virtual ~PortAudioPlayback();
[[nodiscard]] size_t sample_rate() const override;
protected:
bool impl_start(std::string& /* error */) override;
void impl_stop() override;
private:
void write_callback(void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags);
PaDeviceIndex index;
const PaDeviceInfo* info;
PaStream* stream{nullptr};
};
class PortAudioRecord : public AudioDeviceRecord {
public:
static constexpr auto kChannelCount{2};
static constexpr auto kSampleRate{44100};
explicit PortAudioRecord(PaDeviceIndex index, const PaDeviceInfo* info);
virtual ~PortAudioRecord();
[[nodiscard]] size_t sample_rate() const override;
protected:
bool impl_start(std::string& /* error */) override;
void impl_stop() override;
private:
void read_callback(const void *input, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags);
PaDeviceIndex index;
const PaDeviceInfo* info;
PaStream* stream{nullptr};
};
struct PaAudioDevice : public AudioDevice {
public:
explicit PaAudioDevice(PaDeviceIndex index, const PaDeviceInfo* info, const PaHostApiInfo* host);
virtual ~PaAudioDevice() = default;
[[nodiscard]] std::string id() const override;
[[nodiscard]] std::string name() const override;
[[nodiscard]] std::string driver() const override;
[[nodiscard]] bool is_input_supported() const override;
[[nodiscard]] bool is_output_supported() const override;
[[nodiscard]] bool is_input_default() const override;
[[nodiscard]] bool is_output_default() const override;
[[nodiscard]] std::shared_ptr<AudioDevicePlayback> playback() override;
[[nodiscard]] std::shared_ptr<AudioDeviceRecord> record() override;
private:
const PaDeviceIndex _index;
const PaDeviceInfo* _info;
const PaHostApiInfo* _host_info;
std::mutex io_lock{};
std::shared_ptr<PortAudioPlayback> _playback;
std::shared_ptr<PortAudioRecord> _record;
};
extern void initialize();
extern void finalize();
extern std::deque<std::shared_ptr<PaAudioDevice>> devices();
}
@@ -0,0 +1,95 @@
//
// Created by WolverinDEV on 13/02/2020.
//
#include <algorithm>
#include <cmath>
#include "./PortAudio.h"
#include "../../logger.h"
using namespace tc::audio::pa;
PortAudioPlayback::PortAudioPlayback(PaDeviceIndex index, const PaDeviceInfo *info)
: index{index}, info{info} { }
PortAudioPlayback::~PortAudioPlayback() {
this->stop();
}
bool PortAudioPlayback::impl_start(std::string &error) {
//TODO: Detect correct sample rate
{
auto device_info = Pa_GetDeviceInfo(this->index);
if(this->info != device_info) {
error = "invalid info pointer";
return false;
}
}
const auto proxied_write_callback = [](
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData) {
assert(output);
auto player = reinterpret_cast<PortAudioPlayback*>(userData);
assert(player);
player->write_callback(output, frameCount, timeInfo, statusFlags);
return 0;
};
PaStreamParameters parameters{};
memset(&parameters, 0, sizeof(parameters));
parameters.channelCount = (int) kChannelCount;
parameters.device = this->index;
parameters.sampleFormat = paFloat32;
parameters.suggestedLatency = this->info->defaultLowInputLatency;
auto err = Pa_OpenStream(
&this->stream,
nullptr,
&parameters,
(double) kSampleRate,
kSampleRate * kTimeSpan,
paClipOff,
proxied_write_callback,
this);
if(err != paNoError) {
this->stream = nullptr;
error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")";
return false;
}
err = Pa_StartStream(this->stream);
if(err != paNoError) {
error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")";
err = Pa_CloseStream(this->stream);
if(err != paNoError)
log_critical(category::audio, tr("Failed to close opened pa stream. This will cause memory leaks. Error: {}/{}"), err, Pa_GetErrorText(err));
return false;
}
return true;
}
void PortAudioPlayback::impl_stop() {
if(Pa_IsStreamActive(this->stream))
Pa_AbortStream(this->stream);
auto error = Pa_CloseStream(this->stream);
if(error != paNoError)
log_error(category::audio, tr("Failed to close PA stream: {}"), error);
this->stream = nullptr;
}
size_t PortAudioPlayback::sample_rate() const {
return this->info->defaultSampleRate;
}
void PortAudioPlayback::write_callback(void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
this->fill_buffer(output, frameCount, kChannelCount);
}
@@ -0,0 +1,95 @@
//
// Created by WolverinDEV on 13/02/2020.
//
#include <algorithm>
#include <cmath>
#include "./PortAudio.h"
#include "../../logger.h"
using namespace tc::audio::pa;
PortAudioRecord::PortAudioRecord(PaDeviceIndex index, const PaDeviceInfo *info) : index{index}, info{info} {}
PortAudioRecord::~PortAudioRecord() {
this->stop();
}
bool PortAudioRecord::impl_start(std::string &error) {
//TODO: Detect correct sample rate
{
auto device_info = Pa_GetDeviceInfo(this->index);
if(this->info != device_info) {
error = "invalid info pointer";
return false;
}
}
const auto proxied_read_callback = [](
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData) {
assert(input);
auto recorder = reinterpret_cast<PortAudioRecord*>(userData);
assert(recorder);
recorder->read_callback(input, frameCount, timeInfo, statusFlags);
return 0;
};
PaStreamParameters parameters{};
memset(&parameters, 0, sizeof(parameters));
parameters.channelCount = (int) kChannelCount;
parameters.device = this->index;
parameters.sampleFormat = paFloat32;
parameters.suggestedLatency = this->info->defaultLowInputLatency;
auto err = Pa_OpenStream(
&this->stream,
&parameters,
nullptr,
(double) kSampleRate,
paFramesPerBufferUnspecified,
paClipOff,
proxied_read_callback,
this);
if(err != paNoError) {
this->stream = nullptr;
error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")";
return false;
}
err = Pa_StartStream(this->stream);
if(err != paNoError) {
error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")";
err = Pa_CloseStream(this->stream);
if(err != paNoError)
log_critical(category::audio, tr("Failed to close opened pa stream. This will cause memory leaks. Error: {}/{}"), err, Pa_GetErrorText(err));
return false;
}
return true;
}
void PortAudioRecord::impl_stop() {
if(Pa_IsStreamActive(this->stream))
Pa_AbortStream(this->stream);
auto error = Pa_CloseStream(this->stream);
if(error != paNoError)
log_error(category::audio, tr("Failed to close PA stream: {}"), error);
this->stream = nullptr;
}
size_t PortAudioRecord::sample_rate() const {
return this->info->defaultSampleRate;
}
void PortAudioRecord::read_callback(const void *input, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
std::lock_guard consumer_lock{this->consumer_lock};
for(auto& consumer : this->_consumers)
consumer->consume(input, frameCount, kChannelCount);
}
@@ -54,6 +54,7 @@ bool SoundIOPlayback::impl_start(std::string &error) {
return false;
}
this->write_exit = false;
this->stream->userdata = this;
this->stream->sample_rate = this->_sample_rate;
this->stream->format = SoundIoFormatFloat32LE;
@@ -144,7 +145,7 @@ void SoundIOPlayback::write_callback(int frame_count_min, int frame_count_max) {
this->priority_boost = true;
// Attempt to assign "Pro Audio" characteristic to thread
HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );
HMODULE AvrtDll = LoadLibrary((LPCTSTR) "AVRT.dll");
if ( AvrtDll ) {
DWORD taskIndex = 0;
@@ -11,13 +11,13 @@ using namespace tc::audio;
extern tc::audio::AudioOutput* global_audio_output;
NAN_MODULE_INIT(player::init_js) {
Nan::Set(target, Nan::New<v8::String>("current_device").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::current_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("set_device").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("current_device"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::current_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("set_device"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("create_stream").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::create_stream)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("create_stream"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::create_stream)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("get_master_volume").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::get_master_volume)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("set_master_volume").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_master_volume)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("get_master_volume"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::get_master_volume)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("set_master_volume"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_master_volume)).ToLocalChecked());
}
NAN_METHOD(audio::available_devices) {
@@ -34,14 +34,14 @@ NAN_METHOD(audio::available_devices) {
auto device = devices[index];
Nan::Set(device_info, Nan::LocalString("name"), Nan::LocalStringUTF8(device->name()));
Nan::Set(device_info, Nan::New<v8::String>("driver").ToLocalChecked(), Nan::New<v8::String>(device->driver()).ToLocalChecked());
Nan::Set(device_info, Nan::New<v8::String>("device_id").ToLocalChecked(), Nan::New<v8::String>(device->id()).ToLocalChecked());
Nan::Set(device_info, Nan::LocalString("driver"), Nan::LocalStringUTF8(device->driver()));
Nan::Set(device_info, Nan::LocalString("device_id"), Nan::New<v8::String>(device->id()).ToLocalChecked());
Nan::Set(device_info, Nan::New<v8::String>("input_supported").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_input_supported()));
Nan::Set(device_info, Nan::New<v8::String>("output_supported").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_output_supported()));
Nan::Set(device_info, Nan::LocalString("input_supported"), Nan::New<v8::Boolean>(device->is_input_supported()));
Nan::Set(device_info, Nan::LocalString("output_supported"), Nan::New<v8::Boolean>(device->is_output_supported()));
Nan::Set(device_info, Nan::New<v8::String>("input_default").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_input_default()));
Nan::Set(device_info, Nan::New<v8::String>("output_default").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_output_default()));
Nan::Set(device_info, Nan::LocalString("input_default"), Nan::New<v8::Boolean>(device->is_input_default()));
Nan::Set(device_info, Nan::LocalString("output_default"), Nan::New<v8::Boolean>(device->is_output_default()));
Nan::Set(result, index, device_info);
}
+1 -1
View File
@@ -150,7 +150,7 @@ NAN_MODULE_INIT(init) {
}
/* TODO: Test error codes and make the audi playback device configurable */
global_audio_output = new tc::audio::AudioOutput(2, 48000);
global_audio_output = new tc::audio::AudioOutput(2, 44100);
global_audio_output->set_device(default_output);
if(!global_audio_output->playback(error)) {
logger::error(category::audio, "Failed to start audio playback: {}", error);
-2
View File
@@ -1,8 +1,6 @@
#include "./hwuid.h"
#include <tomcrypt.h>
#include <array>
#include <fstream>
#include <misc/strobf.h>
#include <misc/base64.h>
#ifndef WIN32
+170 -7
View File
@@ -3,13 +3,13 @@
//
#include "ring_buffer.h"
#ifdef HAVE_SOUNDIO
using namespace tc;
#include <soundio/soundio.h>
using namespace tc;
ring_buffer::ring_buffer(size_t cap) {
this->handle = soundio_ring_buffer_create(nullptr, cap);
this->handle = soundio_ring_buffer_create(nullptr, (int) cap);
}
ring_buffer::~ring_buffer() {
@@ -33,7 +33,7 @@ char* ring_buffer::write_ptr() {
}
void ring_buffer::advance_write_ptr(size_t bytes) {
soundio_ring_buffer_advance_write_ptr((SoundIoRingBuffer*) this->handle, bytes);
soundio_ring_buffer_advance_write_ptr((SoundIoRingBuffer*) this->handle, (int) bytes);
}
const void* ring_buffer::read_ptr() const {
@@ -41,9 +41,172 @@ const void* ring_buffer::read_ptr() const {
}
void ring_buffer::advance_read_ptr(size_t bytes) {
soundio_ring_buffer_advance_read_ptr((SoundIoRingBuffer*) this->handle, bytes);
soundio_ring_buffer_advance_read_ptr((SoundIoRingBuffer*) this->handle, (int) bytes);
}
void ring_buffer::clear() {
soundio_ring_buffer_clear((SoundIoRingBuffer*) this->handle);
}
}
#else
#include <windows.h>
#include <objbase.h>
#include <cassert>
namespace tc {
bool sysinfo_initialized{false};
static SYSTEM_INFO win32_system_info;
int soundio_os_page_size() {
if(!sysinfo_initialized) {
GetSystemInfo(&win32_system_info);
sysinfo_initialized = true;
}
return win32_system_info.dwAllocationGranularity;
}
static inline size_t ceil_dbl_to_size_t(double x) {
const double truncation = (size_t)x;
return truncation + (truncation < x);
}
ring_buffer::ring_buffer(size_t min_capacity) {
this->allocate_memory(min_capacity);
}
ring_buffer::~ring_buffer() {
this->free_memory();
}
bool ring_buffer::allocate_memory(size_t requested_capacity) {
size_t actual_capacity = ceil_dbl_to_size_t(requested_capacity / (double) soundio_os_page_size()) * soundio_os_page_size();
this->memory.address = nullptr;
#ifdef WIN32
BOOL ok;
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, actual_capacity * 2, nullptr);
if (!hMapFile) return false;
for (;;) {
// find a free address space with the correct size
char *address = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity * 2);
if (!address) {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
// found a big enough address space. hopefully it will remain free
// while we map to it. if not, we'll try again.
ok = UnmapViewOfFile(address);
assert(ok);
char *addr1 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity, address);
if (addr1 != address) {
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
}
char *addr2 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_WRITE, 0, 0,
actual_capacity, address + actual_capacity);
if (addr2 != address + actual_capacity) {
ok = UnmapViewOfFile(addr1);
assert(ok);
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
}
this->memory.priv = hMapFile;
this->memory.address = address;
break;
}
#else
#error "Implement me!"
#endif
this->memory.capacity = actual_capacity;
this->_capacity = actual_capacity;
return true;
}
void ring_buffer::free_memory() {
if(!this->memory.address) return;
#ifdef WIN32
BOOL ok;
ok = UnmapViewOfFile(this->memory.address);
assert(ok);
ok = UnmapViewOfFile(this->memory.address + this->memory.capacity);
assert(ok);
ok = CloseHandle((HANDLE) this->memory.priv);
assert(ok);
#else
#error "Implement me!"
#endif
this->memory.address = nullptr;
}
size_t ring_buffer::capacity() const {
return this->_capacity;
}
char* ring_buffer::write_ptr() {
auto offset{this->write_offset.load()};
return this->memory.address + (offset % this->memory.capacity);
}
void ring_buffer::advance_write_ptr(size_t bytes) {
this->write_offset.fetch_add(bytes);
assert(this->fill_count() >= 0);
}
const void* ring_buffer::read_ptr() const {
auto offset{this->read_offset.load()};
return this->memory.address + (offset % this->memory.capacity);
}
void ring_buffer::advance_read_ptr(size_t bytes) {
this->read_offset.fetch_add(bytes);
assert(this->fill_count() >= 0);
}
size_t ring_buffer::fill_count() const {
// Whichever offset we load first might have a smaller value. So we load
// the read_offset first.
auto roffset{this->read_offset.load()};
auto woffset{this->write_offset.load()};
int count = woffset - roffset;
assert(count >= 0);
assert(count <= this->memory.capacity);
return count;
}
size_t ring_buffer::free_count() const {
return this->memory.capacity - this->fill_count();
}
void ring_buffer::clear() {
this->write_offset.store(this->read_offset.load());
}
bool ring_buffer::valid() const {
return this->memory.address != nullptr;
}
}
#endif
+21
View File
@@ -1,6 +1,7 @@
#pragma once
#include <cstddef>
#include <atomic>
namespace tc {
class ring_buffer {
@@ -22,7 +23,27 @@ namespace tc {
void advance_read_ptr(size_t /* count */);
void clear();
[[nodiscard]] bool valid() const;
[[nodiscard]] inline operator bool() const { return this->valid(); }
private:
#ifndef HAVE_SOUNDIO
struct MirroredMemory {
size_t capacity;
char *address;
void *priv;
};
bool allocate_memory(size_t requested_capacity);
void free_memory();
MirroredMemory memory{};
std::atomic_long write_offset;
std::atomic_long read_offset;
size_t _capacity{0}; /* for faster access */
#endif
void* handle{nullptr};
};
}