Stashing some changes

This commit is contained in:
WolverinDEV 2020-02-09 13:15:21 +01:00
parent 28225b5f48
commit 105eb52eef
12 changed files with 170 additions and 38 deletions

View File

@ -110,7 +110,7 @@ if (MSVC)
foreach(CompilerFlag ${CompilerFlags}) foreach(CompilerFlag ${CompilerFlags})
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
endforeach() endforeach()
add_compile_options("/MTd") #add_compile_options("/MTd")
add_compile_options("/EHsc") #We require exception handling add_compile_options("/EHsc") #We require exception handling
else() else()
#This is a bad thing here! #This is a bad thing here!

View File

@ -1,5 +1,6 @@
#include "AudioOutput.h" #include "./AudioOutput.h"
#include "AudioMerger.h" #include "./AudioMerger.h"
#include "./AudioResampler.h"
#include "../logger.h" #include "../logger.h"
#include <cstring> #include <cstring>
#include <algorithm> #include <algorithm>
@ -170,6 +171,7 @@ void AudioOutput::cleanup_buffers() {
} }
void AudioOutput::fill_buffer(void *output, size_t frameCount, size_t channels) { void AudioOutput::fill_buffer(void *output, size_t frameCount, size_t channels) {
const auto local_frame_count = this->_resampler ? this->_resampler->
lock_guard buffer_lock(this->buffer_lock); lock_guard buffer_lock(this->buffer_lock);
if(this->_volume <= 0) { if(this->_volume <= 0) {
for(auto& source : this->_sources) for(auto& source : this->_sources)

View File

@ -101,6 +101,7 @@ namespace tc::audio {
std::recursive_mutex device_lock; std::recursive_mutex device_lock;
std::shared_ptr<AudioDevice> device{nullptr}; std::shared_ptr<AudioDevice> device{nullptr};
std::shared_ptr<AudioDevicePlayback> _playback{nullptr}; std::shared_ptr<AudioDevicePlayback> _playback{nullptr};
std::unique_ptr<AudioResampler> _resampler{nullptr};
std::mutex buffer_lock; /* not required, but why not. Usually only used within audio_callback! */ std::mutex buffer_lock; /* not required, but why not. Usually only used within audio_callback! */
void* source_buffer = nullptr; void* source_buffer = nullptr;

View File

@ -13,6 +13,11 @@
using namespace tc::audio; using namespace tc::audio;
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>> devices() {
std::deque<std::shared_ptr<AudioDevice>> result{}; std::deque<std::shared_ptr<AudioDevice>> result{};
for(auto& backend : SoundIOBackendHandler::all_backends()) { for(auto& backend : SoundIOBackendHandler::all_backends()) {
@ -22,6 +27,13 @@ namespace tc::audio {
result.insert(result.end(), input_devices.begin(), input_devices.end()); result.insert(result.end(), input_devices.begin(), input_devices.end());
result.insert(result.end(), output_devices.begin(), output_devices.end()); result.insert(result.end(), output_devices.begin(), output_devices.end());
} }
#ifdef WIN32
//Remove all not raw devices. We do not support shared mode yet
result.erase(std::remove_if(result.begin(), result.end(), [](const std::shared_ptr<AudioDevice>& device) {
if(device->driver() != "WASAPI") return false;
return !ends_with(device->id(), "_raw");
}), result.end());
#endif
return result; return result;
} }

View File

@ -14,6 +14,8 @@ namespace tc::audio {
virtual void consume(const void* /* buffer */, size_t /* samples */, size_t /* channel count */) = 0; virtual void consume(const void* /* buffer */, size_t /* samples */, size_t /* channel count */) = 0;
}; };
[[nodiscard]] virtual size_t sample_rate() const = 0;
[[nodiscard]] bool start(std::string& /* error */); [[nodiscard]] bool start(std::string& /* error */);
void stop_if_possible(); void stop_if_possible();
void stop(); void stop();
@ -43,6 +45,8 @@ namespace tc::audio {
virtual void fill_buffer(void* /* target */, size_t /* samples */, size_t /* channel count */) = 0; virtual void fill_buffer(void* /* target */, size_t /* samples */, size_t /* channel count */) = 0;
}; };
[[nodiscard]] virtual size_t sample_rate() const = 0;
[[nodiscard]] bool start(std::string& /* error */); [[nodiscard]] bool start(std::string& /* error */);
void stop_if_possible(); void stop_if_possible();
void stop(); void stop();
@ -52,6 +56,8 @@ namespace tc::audio {
return this->_sources; return this->_sources;
} }
void register_source(Source* /* source */); void register_source(Source* /* source */);
/* will and must be blocking until audio callback is done */
void remove_source(Source* /* source */); void remove_source(Source* /* source */);
protected: protected:

View File

@ -134,6 +134,13 @@ void SoundIOBackendHandler::disconnect() {
soundio_disconnect(this->soundio_handle); soundio_disconnect(this->soundio_handle);
} }
inline std::string sample_rates(struct ::SoundIoDevice *dev) {
std::string result{};
for(size_t index = 0; index < dev->sample_rate_count; index++)
result += (index > 0 ? ", [" : "[") + std::to_string(dev->sample_rates[index].min) + ";" + std::to_string(dev->sample_rates[index].max) + "]";
return dev->sample_rate_count ? result : "none";
}
void SoundIOBackendHandler::handle_device_change() { void SoundIOBackendHandler::handle_device_change() {
log_debug(category::audio, tr("Device list changed for backend {}. Reindexing devices."), this->name()); log_debug(category::audio, tr("Device list changed for backend {}. Reindexing devices."), this->name());
@ -160,7 +167,7 @@ void SoundIOBackendHandler::handle_device_change() {
} }
auto device = std::make_shared<SoundIODevice>(dev, this->name(), i == default_input_device, true); auto device = std::make_shared<SoundIODevice>(dev, this->name(), i == default_input_device, true);
log_trace(category::audio, tr("Found input device {} ({})."), dev->id, dev->name); log_trace(category::audio, tr("Found input device {} ({}). Raw: {}. Rates: {}"), dev->id, dev->name, dev->is_raw, sample_rates(dev));
this->cached_input_devices.push_back(device); this->cached_input_devices.push_back(device);
if(i == default_input_device) if(i == default_input_device)
this->_default_input_device = device; this->_default_input_device = device;
@ -181,7 +188,7 @@ void SoundIOBackendHandler::handle_device_change() {
} }
auto device = std::make_shared<SoundIODevice>(dev, this->name(), i == default_output_device, true); auto device = std::make_shared<SoundIODevice>(dev, this->name(), i == default_output_device, true);
log_trace(category::audio, tr("Found output device {} ({})."), dev->id, dev->name); log_trace(category::audio, tr("Found output device {} ({}). Raw: {}. Rates: {}."), dev->id, dev->name, dev->is_raw, sample_rates(dev));
this->cached_output_devices.push_back(device); this->cached_output_devices.push_back(device);
if(i == default_output_device) if(i == default_output_device)
this->_default_output_device = device; this->_default_output_device = device;
@ -201,6 +208,12 @@ void SoundIOBackendHandler::handle_backend_disconnect(int error) {
SoundIODevice::SoundIODevice(struct ::SoundIoDevice *dev, std::string driver, bool default_, bool owned) : device_handle{dev}, driver_name{std::move(driver)}, _default{default_} { SoundIODevice::SoundIODevice(struct ::SoundIoDevice *dev, std::string driver, bool default_, bool owned) : device_handle{dev}, driver_name{std::move(driver)}, _default{default_} {
if(!owned) soundio_device_ref(dev); if(!owned) soundio_device_ref(dev);
if(this->device_handle->is_raw) {
this->_device_id = std::string{dev->id} + "_raw";
} else {
this->_device_id = dev->id;
}
} }
SoundIODevice::~SoundIODevice() { SoundIODevice::~SoundIODevice() {
@ -208,7 +221,7 @@ SoundIODevice::~SoundIODevice() {
} }
std::string SoundIODevice::id() const { std::string SoundIODevice::id() const {
return this->device_handle->id; return this->_device_id;
} }
std::string SoundIODevice::name() const { std::string SoundIODevice::name() const {

View File

@ -26,19 +26,25 @@ namespace tc::audio {
} }
}; };
constexpr std::array kSampleRateOrder{48000, 44100};
constexpr auto kDefaultSampleRate{kSampleRateOrder[0]};
class SoundIOPlayback : public AudioDevicePlayback { class SoundIOPlayback : public AudioDevicePlayback {
public: public:
constexpr static auto kChunkSize{960}; constexpr static auto kChunkTime{0.02};
explicit SoundIOPlayback(struct ::SoundIoDevice* /* handle */); explicit SoundIOPlayback(struct ::SoundIoDevice* /* handle */);
virtual ~SoundIOPlayback(); virtual ~SoundIOPlayback();
size_t sample_rate() const override;
protected: protected:
bool impl_start(std::string& /* error */) override; bool impl_start(std::string& /* error */) override;
void impl_stop() override; void impl_stop() override;
private: private:
size_t _sample_rate;
bool stream_invalid{false}; bool stream_invalid{false};
bool have_underflow{false};
struct ::SoundIoDevice* device_handle{nullptr}; struct ::SoundIoDevice* device_handle{nullptr};
struct ::SoundIoOutStream* stream{nullptr}; struct ::SoundIoOutStream* stream{nullptr};
@ -54,11 +60,13 @@ namespace tc::audio {
explicit SoundIORecord(struct ::SoundIoDevice* /* handle */); explicit SoundIORecord(struct ::SoundIoDevice* /* handle */);
virtual ~SoundIORecord(); virtual ~SoundIORecord();
size_t sample_rate() const override;
protected: protected:
bool impl_start(std::string& /* error */) override; bool impl_start(std::string& /* error */) override;
void impl_stop() override; void impl_stop() override;
private: private:
size_t _sample_rate;
bool stream_invalid{false}; bool stream_invalid{false};
struct ::SoundIoDevice* device_handle{nullptr}; struct ::SoundIoDevice* device_handle{nullptr};
struct ::SoundIoInStream* stream{nullptr}; struct ::SoundIoInStream* stream{nullptr};
@ -86,6 +94,8 @@ namespace tc::audio {
[[nodiscard]] std::shared_ptr<AudioDevicePlayback> playback() override; [[nodiscard]] std::shared_ptr<AudioDevicePlayback> playback() override;
[[nodiscard]] std::shared_ptr<AudioDeviceRecord> record() override; [[nodiscard]] std::shared_ptr<AudioDeviceRecord> record() override;
private: private:
std::string _device_id{};
std::string driver_name{}; std::string driver_name{};
struct ::SoundIoDevice* device_handle{nullptr}; struct ::SoundIoDevice* device_handle{nullptr};
bool _default{false}; bool _default{false};

View File

@ -2,29 +2,51 @@
// Created by wolverindev on 07.02.20. // Created by wolverindev on 07.02.20.
// //
#include "SoundIO.h"
#include <algorithm> #include <algorithm>
#include <cmath>
#include "SoundIO.h"
#include "../../logger.h" #include "../../logger.h"
using namespace tc::audio; using namespace tc::audio;
SoundIOPlayback::SoundIOPlayback(struct ::SoundIoDevice *device) : device_handle{device} { SoundIOPlayback::SoundIOPlayback(struct ::SoundIoDevice *device) : device_handle{device} {
soundio_device_ref(device); soundio_device_ref(device);
if(device->probe_error || !device->sample_rate_count)
this->_sample_rate = kDefaultSampleRate;
else {
for(const auto& sample_rate : kSampleRateOrder) {
for(size_t index{0}; index < device->sample_rate_count; index++) {
auto supported_rate = device->sample_rates[index];
if(supported_rate.min <= sample_rate && supported_rate.max >= sample_rate) {
this->_sample_rate = sample_rate;
goto _found;
}
}
}
this->_sample_rate = kDefaultSampleRate;
_found:;
}
} }
SoundIOPlayback::~SoundIOPlayback() { SoundIOPlayback::~SoundIOPlayback() {
soundio_device_unref(this->device_handle); soundio_device_unref(this->device_handle);
} }
size_t SoundIOPlayback::sample_rate() const {
return this->_sample_rate;
}
bool SoundIOPlayback::impl_start(std::string &error) { bool SoundIOPlayback::impl_start(std::string &error) {
assert(this->device_handle); assert(this->device_handle);
this->buffer = soundio_ring_buffer_create(nullptr, kChunkSize * sizeof(float) * 2); /* 2 channels */ //TODO: Figure out how many channels!
this->buffer = soundio_ring_buffer_create(nullptr, (int) (kChunkTime * this->_sample_rate * sizeof(float) * 2)); /* 2 channels */
if(!buffer) { if(!buffer) {
error = "failed to allocate the buffer"; error = "failed to allocate the buffer";
return false; return false;
} }
soundio_ring_buffer_clear(this->buffer);
this->stream = soundio_outstream_create(this->device_handle); this->stream = soundio_outstream_create(this->device_handle);
if(!this->stream) { if(!this->stream) {
@ -33,6 +55,7 @@ bool SoundIOPlayback::impl_start(std::string &error) {
} }
this->stream->userdata = this; this->stream->userdata = this;
this->stream->sample_rate = this->_sample_rate;
this->stream->format = SoundIoFormatFloat32LE; this->stream->format = SoundIoFormatFloat32LE;
this->stream->software_latency = 0.02; this->stream->software_latency = 0.02;
@ -54,28 +77,37 @@ bool SoundIOPlayback::impl_start(std::string &error) {
if(auto err = soundio_outstream_open(this->stream); err) { if(auto err = soundio_outstream_open(this->stream); err) {
error = soundio_strerror(err) + std::string{" (open)"}; error = soundio_strerror(err) + std::string{" (open)"};
soundio_outstream_destroy(this->stream); goto error_cleanup;
this->stream = nullptr; }
return false;
if(this->_sample_rate != this->stream->sample_rate) {
error = "sample rate mismatch (" + std::to_string(this->_sample_rate) + " <> " + std::to_string(this->stream->sample_rate) + ")";
goto error_cleanup;
} }
if(false && this->stream->layout_error) { if(false && this->stream->layout_error) {
error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error); error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error);
soundio_outstream_destroy(this->stream); goto error_cleanup;
this->stream = nullptr;
return false;
} }
if(auto err = soundio_outstream_start(this->stream); err) { if(auto err = soundio_outstream_start(this->stream); err) {
error = soundio_strerror(err) + std::string{" (start)"}; error = soundio_strerror(err) + std::string{" (start)"};
soundio_outstream_destroy(this->stream); goto error_cleanup;
this->stream = nullptr;
return false;
} }
soundio_outstream_wasapi_set_sleep_divider(this->stream,100); // Play one quarter and then request fill again so we've 3 quarters to fill up again
//TODO: Test for interleaved channel layout! //TODO: Test for interleaved channel layout!
return true; return true;
error_cleanup:
if(this->stream) soundio_outstream_destroy(this->stream);
this->stream = nullptr;
if(this->buffer) soundio_ring_buffer_destroy(this->buffer);
this->buffer = nullptr;
return false;
} }
void SoundIOPlayback::impl_stop() { void SoundIOPlayback::impl_stop() {
@ -94,10 +126,28 @@ void SoundIOPlayback::write_callback(int frame_count_min, int frame_count_max) {
struct SoundIoChannelArea *areas; struct SoundIoChannelArea *areas;
int frames_left{frame_count_min}, err; int frames_left{frame_count_min}, err;
if(frames_left < 120) const auto min_interval = this->have_underflow ? 0.02 : 0.01;
frames_left = 120; const auto max_latency = 0.02;
if(frames_left > frame_count_max)
frames_left = frame_count_max;
{
const auto _min_interval_frames = (int) (min_interval * this->stream->sample_rate + .5);
if(frames_left < _min_interval_frames)
frames_left = _min_interval_frames;
if(frames_left > frame_count_max)
frames_left = frame_count_max;
if(frame_count_max == 0) return;
}
{
double latency{};
if(auto err = soundio_outstream_get_latency(this->stream, &latency); err) {
log_warn(category::audio, tr("Failed to get auto stream latency: {}"), err);
return;
}
if(latency > max_latency) return;
}
while(frames_left > 0) { while(frames_left > 0) {
int frame_count{frames_left}; int frame_count{frames_left};

View File

@ -10,12 +10,33 @@ using namespace tc::audio;
SoundIORecord::SoundIORecord(struct ::SoundIoDevice *device) : device_handle{device} { SoundIORecord::SoundIORecord(struct ::SoundIoDevice *device) : device_handle{device} {
soundio_device_ref(device); soundio_device_ref(device);
if(device->probe_error || !device->sample_rate_count)
this->_sample_rate = kDefaultSampleRate;
else {
for(const auto& sample_rate : kSampleRateOrder) {
for(size_t index{0}; index < device->sample_rate_count; index++) {
auto supported_rate = device->sample_rates[index];
if(supported_rate.min <= sample_rate && supported_rate.max >= sample_rate) {
this->_sample_rate = sample_rate;
goto _found;
}
}
}
this->_sample_rate = kDefaultSampleRate;
_found:;
}
} }
SoundIORecord::~SoundIORecord() { SoundIORecord::~SoundIORecord() {
soundio_device_unref(this->device_handle); soundio_device_unref(this->device_handle);
} }
size_t SoundIORecord::sample_rate() const {
return this->_sample_rate;
}
bool SoundIORecord::impl_start(std::string &error) { bool SoundIORecord::impl_start(std::string &error) {
assert(this->device_handle); assert(this->device_handle);
@ -34,6 +55,7 @@ bool SoundIORecord::impl_start(std::string &error) {
this->stream->userdata = this; this->stream->userdata = this;
this->stream->format = SoundIoFormatFloat32LE; this->stream->format = SoundIoFormatFloat32LE;
this->stream->sample_rate = this->_sample_rate;
this->stream->software_latency = 0.02; this->stream->software_latency = 0.02;
this->stream->overflow_callback = [](auto str) { this->stream->overflow_callback = [](auto str) {
@ -54,28 +76,36 @@ bool SoundIORecord::impl_start(std::string &error) {
if(auto err = soundio_instream_open(this->stream); err) { if(auto err = soundio_instream_open(this->stream); err) {
error = soundio_strerror(err) + std::string{" (open)"}; error = soundio_strerror(err) + std::string{" (open)"};
soundio_instream_destroy(this->stream); goto error_cleanup;
this->stream = nullptr; }
return false;
if(this->_sample_rate != this->stream->sample_rate) {
error = "sample rate mismatch (" + std::to_string(this->_sample_rate) + " <> " + std::to_string(this->stream->sample_rate) + ")";
goto error_cleanup;
} }
if(false && this->stream->layout_error) { if(false && this->stream->layout_error) {
error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error); error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error);
soundio_instream_destroy(this->stream); goto error_cleanup;
this->stream = nullptr;
return false;
} }
if(auto err = soundio_instream_start(this->stream); err) { if(auto err = soundio_instream_start(this->stream); err) {
error = soundio_strerror(err) + std::string{" (start)"}; error = soundio_strerror(err) + std::string{" (start)"};
soundio_instream_destroy(this->stream); goto error_cleanup;
this->stream = nullptr;
return false;
} }
//TODO: Test for interleaved channel layout! //TODO: Test for interleaved channel layout!
return true; return true;
error_cleanup:
if(this->stream) soundio_instream_destroy(this->stream);
this->stream = nullptr;
if(this->buffer) soundio_ring_buffer_destroy(this->buffer);
this->buffer = nullptr;
return false;
} }
void SoundIORecord::impl_stop() { void SoundIORecord::impl_stop() {

View File

@ -1,5 +1,4 @@
#include "ProtocolHandler.h" #include "ProtocolHandler.h"
#include "ServerConnection.h"
#include "Socket.h" #include "Socket.h"
#include "../logger.h" #include "../logger.h"
#include "Error.h" #include "Error.h"
@ -7,7 +6,6 @@
#include <misc/endianness.h> #include <misc/endianness.h>
#include <protocol/buffers.h> #include <protocol/buffers.h>
#include <thread> #include <thread>
#include <iostream>
#include <tomcrypt.h> #include <tomcrypt.h>
#include <tommath.h> #include <tommath.h>

View File

@ -147,10 +147,21 @@ void UDPSocket::callback_read(evutil_socket_t fd) {
source_address_length = sizeof(sockaddr); source_address_length = sizeof(sockaddr);
read_length = recvfrom(fd, (char*) buffer, (int) buffer_length, MSG_DONTWAIT, &source_address, &source_address_length); read_length = recvfrom(fd, (char*) buffer, (int) buffer_length, MSG_DONTWAIT, &source_address, &source_address_length);
if(read_length <= 0) { if(read_length <= 0) {
if(errno == EAGAIN || (read_length == 0 && read_count > 0)) if(read_length == 0 && read_count > 0)
break; break;
logger::warn(category::socket, tr("Failed to receive data: {}/{}"), errno, strerror(errno)); int error;
#ifdef WIN32
error = WSAGetLastError();
if(error == WSAEWOULDBLOCK)
break;
#else
error = errno;
if(errno == EAGAIN)
break;
#endif
logger::warn(category::socket, tr("Failed to receive data: {}"), error);
break; /* this should never happen! */ break; /* this should never happen! */
} }

View File

@ -10,7 +10,6 @@ void force_log_node(logger::category::value, spdlog::level::level_enum, const st
#ifdef NODEJS_API #ifdef NODEJS_API
#include <NanGet.h> #include <NanGet.h>
#include <include/NanEventCallback.h> #include <include/NanEventCallback.h>
#include <include/NanStrings.h>
/* NODE JS */ /* NODE JS */
struct LogMessage { struct LogMessage {