From 105eb52eef30221dbabf2b7efc58f8fa21cc02c1 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sun, 9 Feb 2020 13:15:21 +0100 Subject: [PATCH] Stashing some changes --- native/CMakeLists.txt | 2 +- .../src/audio/AudioOutput.cpp | 6 +- .../serverconnection/src/audio/AudioOutput.h | 1 + .../src/audio/driver/AudioDriver.cpp | 12 +++ .../src/audio/driver/AudioDriver.h | 6 ++ .../src/audio/driver/SoundIO.cpp | 19 ++++- .../src/audio/driver/SoundIO.h | 12 ++- .../src/audio/driver/SoundIOPlayback.cpp | 82 +++++++++++++++---- .../src/audio/driver/SoundIORecord.cpp | 48 +++++++++-- .../src/connection/ProtocolHandlerPOW.cpp | 2 - .../src/connection/Socket.cpp | 17 +++- native/serverconnection/src/logger.cpp | 1 - 12 files changed, 170 insertions(+), 38 deletions(-) diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index bdd537a..41b6abb 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -110,7 +110,7 @@ if (MSVC) foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") endforeach() - add_compile_options("/MTd") + #add_compile_options("/MTd") add_compile_options("/EHsc") #We require exception handling else() #This is a bad thing here! diff --git a/native/serverconnection/src/audio/AudioOutput.cpp b/native/serverconnection/src/audio/AudioOutput.cpp index 5c31eb9..7be078d 100644 --- a/native/serverconnection/src/audio/AudioOutput.cpp +++ b/native/serverconnection/src/audio/AudioOutput.cpp @@ -1,5 +1,6 @@ -#include "AudioOutput.h" -#include "AudioMerger.h" +#include "./AudioOutput.h" +#include "./AudioMerger.h" +#include "./AudioResampler.h" #include "../logger.h" #include #include @@ -170,6 +171,7 @@ void AudioOutput::cleanup_buffers() { } 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); if(this->_volume <= 0) { for(auto& source : this->_sources) diff --git a/native/serverconnection/src/audio/AudioOutput.h b/native/serverconnection/src/audio/AudioOutput.h index 7468625..48b1ba9 100644 --- a/native/serverconnection/src/audio/AudioOutput.h +++ b/native/serverconnection/src/audio/AudioOutput.h @@ -101,6 +101,7 @@ namespace tc::audio { std::recursive_mutex device_lock; std::shared_ptr device{nullptr}; std::shared_ptr _playback{nullptr}; + std::unique_ptr _resampler{nullptr}; std::mutex buffer_lock; /* not required, but why not. Usually only used within audio_callback! */ void* source_buffer = nullptr; diff --git a/native/serverconnection/src/audio/driver/AudioDriver.cpp b/native/serverconnection/src/audio/driver/AudioDriver.cpp index 5c7f808..3a1499a 100644 --- a/native/serverconnection/src/audio/driver/AudioDriver.cpp +++ b/native/serverconnection/src/audio/driver/AudioDriver.cpp @@ -13,6 +13,11 @@ 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> devices() { std::deque> result{}; 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(), 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& device) { + if(device->driver() != "WASAPI") return false; + return !ends_with(device->id(), "_raw"); + }), result.end()); +#endif return result; } diff --git a/native/serverconnection/src/audio/driver/AudioDriver.h b/native/serverconnection/src/audio/driver/AudioDriver.h index aabbec0..bd6d392 100644 --- a/native/serverconnection/src/audio/driver/AudioDriver.h +++ b/native/serverconnection/src/audio/driver/AudioDriver.h @@ -14,6 +14,8 @@ namespace tc::audio { 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 */); void stop_if_possible(); void stop(); @@ -43,6 +45,8 @@ namespace tc::audio { 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 */); void stop_if_possible(); void stop(); @@ -52,6 +56,8 @@ namespace tc::audio { return this->_sources; } void register_source(Source* /* source */); + + /* will and must be blocking until audio callback is done */ void remove_source(Source* /* source */); protected: diff --git a/native/serverconnection/src/audio/driver/SoundIO.cpp b/native/serverconnection/src/audio/driver/SoundIO.cpp index fed7dda..d4ee536 100644 --- a/native/serverconnection/src/audio/driver/SoundIO.cpp +++ b/native/serverconnection/src/audio/driver/SoundIO.cpp @@ -134,6 +134,13 @@ void SoundIOBackendHandler::disconnect() { 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() { 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(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); if(i == default_input_device) this->_default_input_device = device; @@ -181,7 +188,7 @@ void SoundIOBackendHandler::handle_device_change() { } auto device = std::make_shared(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); if(i == default_output_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_} { 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() { @@ -208,7 +221,7 @@ SoundIODevice::~SoundIODevice() { } std::string SoundIODevice::id() const { - return this->device_handle->id; + return this->_device_id; } std::string SoundIODevice::name() const { diff --git a/native/serverconnection/src/audio/driver/SoundIO.h b/native/serverconnection/src/audio/driver/SoundIO.h index c26dbb4..a3e7993 100644 --- a/native/serverconnection/src/audio/driver/SoundIO.h +++ b/native/serverconnection/src/audio/driver/SoundIO.h @@ -26,19 +26,25 @@ namespace tc::audio { } }; + constexpr std::array kSampleRateOrder{48000, 44100}; + constexpr auto kDefaultSampleRate{kSampleRateOrder[0]}; + class SoundIOPlayback : public AudioDevicePlayback { public: - constexpr static auto kChunkSize{960}; + constexpr static auto kChunkTime{0.02}; explicit SoundIOPlayback(struct ::SoundIoDevice* /* handle */); virtual ~SoundIOPlayback(); + size_t sample_rate() const override; protected: bool impl_start(std::string& /* error */) override; void impl_stop() override; private: + size_t _sample_rate; bool stream_invalid{false}; + bool have_underflow{false}; struct ::SoundIoDevice* device_handle{nullptr}; struct ::SoundIoOutStream* stream{nullptr}; @@ -54,11 +60,13 @@ namespace tc::audio { explicit SoundIORecord(struct ::SoundIoDevice* /* handle */); virtual ~SoundIORecord(); + size_t sample_rate() const override; protected: bool impl_start(std::string& /* error */) override; void impl_stop() override; private: + size_t _sample_rate; bool stream_invalid{false}; struct ::SoundIoDevice* device_handle{nullptr}; struct ::SoundIoInStream* stream{nullptr}; @@ -86,6 +94,8 @@ namespace tc::audio { [[nodiscard]] std::shared_ptr playback() override; [[nodiscard]] std::shared_ptr record() override; private: + std::string _device_id{}; + std::string driver_name{}; struct ::SoundIoDevice* device_handle{nullptr}; bool _default{false}; diff --git a/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp b/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp index 89f41ae..aca6800 100644 --- a/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp +++ b/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp @@ -2,29 +2,51 @@ // Created by wolverindev on 07.02.20. // -#include "SoundIO.h" #include +#include +#include "SoundIO.h" #include "../../logger.h" using namespace tc::audio; SoundIOPlayback::SoundIOPlayback(struct ::SoundIoDevice *device) : device_handle{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() { soundio_device_unref(this->device_handle); } +size_t SoundIOPlayback::sample_rate() const { + return this->_sample_rate; +} + bool SoundIOPlayback::impl_start(std::string &error) { 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) { error = "failed to allocate the buffer"; return false; } - soundio_ring_buffer_clear(this->buffer); this->stream = soundio_outstream_create(this->device_handle); if(!this->stream) { @@ -33,6 +55,7 @@ bool SoundIOPlayback::impl_start(std::string &error) { } this->stream->userdata = this; + this->stream->sample_rate = this->_sample_rate; this->stream->format = SoundIoFormatFloat32LE; 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) { error = soundio_strerror(err) + std::string{" (open)"}; - soundio_outstream_destroy(this->stream); - this->stream = nullptr; - return false; + goto error_cleanup; + } + + 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) { error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error); - soundio_outstream_destroy(this->stream); - this->stream = nullptr; - return false; + goto error_cleanup; } if(auto err = soundio_outstream_start(this->stream); err) { error = soundio_strerror(err) + std::string{" (start)"}; - soundio_outstream_destroy(this->stream); - this->stream = nullptr; - return false; + goto error_cleanup; } + 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! 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() { @@ -94,10 +126,28 @@ void SoundIOPlayback::write_callback(int frame_count_min, int frame_count_max) { struct SoundIoChannelArea *areas; int frames_left{frame_count_min}, err; - if(frames_left < 120) - frames_left = 120; - if(frames_left > frame_count_max) - frames_left = frame_count_max; + const auto min_interval = this->have_underflow ? 0.02 : 0.01; + const auto max_latency = 0.02; + + + { + 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) { int frame_count{frames_left}; diff --git a/native/serverconnection/src/audio/driver/SoundIORecord.cpp b/native/serverconnection/src/audio/driver/SoundIORecord.cpp index c13b6f3..98d147e 100644 --- a/native/serverconnection/src/audio/driver/SoundIORecord.cpp +++ b/native/serverconnection/src/audio/driver/SoundIORecord.cpp @@ -10,12 +10,33 @@ using namespace tc::audio; SoundIORecord::SoundIORecord(struct ::SoundIoDevice *device) : device_handle{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() { soundio_device_unref(this->device_handle); } +size_t SoundIORecord::sample_rate() const { + return this->_sample_rate; +} + bool SoundIORecord::impl_start(std::string &error) { assert(this->device_handle); @@ -34,6 +55,7 @@ bool SoundIORecord::impl_start(std::string &error) { this->stream->userdata = this; this->stream->format = SoundIoFormatFloat32LE; + this->stream->sample_rate = this->_sample_rate; this->stream->software_latency = 0.02; 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) { error = soundio_strerror(err) + std::string{" (open)"}; - soundio_instream_destroy(this->stream); - this->stream = nullptr; - return false; + goto error_cleanup; + } + + 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) { error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error); - soundio_instream_destroy(this->stream); - this->stream = nullptr; - return false; + goto error_cleanup; } if(auto err = soundio_instream_start(this->stream); err) { error = soundio_strerror(err) + std::string{" (start)"}; - soundio_instream_destroy(this->stream); - this->stream = nullptr; - return false; + goto error_cleanup; } //TODO: Test for interleaved channel layout! 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() { diff --git a/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp b/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp index 3679673..5330a6a 100644 --- a/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp +++ b/native/serverconnection/src/connection/ProtocolHandlerPOW.cpp @@ -1,5 +1,4 @@ #include "ProtocolHandler.h" -#include "ServerConnection.h" #include "Socket.h" #include "../logger.h" #include "Error.h" @@ -7,7 +6,6 @@ #include #include #include -#include #include #include diff --git a/native/serverconnection/src/connection/Socket.cpp b/native/serverconnection/src/connection/Socket.cpp index 89af66b..3c06b77 100644 --- a/native/serverconnection/src/connection/Socket.cpp +++ b/native/serverconnection/src/connection/Socket.cpp @@ -147,10 +147,21 @@ void UDPSocket::callback_read(evutil_socket_t fd) { source_address_length = sizeof(sockaddr); read_length = recvfrom(fd, (char*) buffer, (int) buffer_length, MSG_DONTWAIT, &source_address, &source_address_length); if(read_length <= 0) { - if(errno == EAGAIN || (read_length == 0 && read_count > 0)) - break; + if(read_length == 0 && read_count > 0) + 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! */ } diff --git a/native/serverconnection/src/logger.cpp b/native/serverconnection/src/logger.cpp index b03125c..a49ac0f 100644 --- a/native/serverconnection/src/logger.cpp +++ b/native/serverconnection/src/logger.cpp @@ -10,7 +10,6 @@ void force_log_node(logger::category::value, spdlog::level::level_enum, const st #ifdef NODEJS_API #include #include -#include /* NODE JS */ struct LogMessage {