Stashing some changes
This commit is contained in:
parent
28225b5f48
commit
105eb52eef
@ -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!
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "AudioOutput.h"
|
||||
#include "AudioMerger.h"
|
||||
#include "./AudioOutput.h"
|
||||
#include "./AudioMerger.h"
|
||||
#include "./AudioResampler.h"
|
||||
#include "../logger.h"
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
@ -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)
|
||||
|
@ -101,6 +101,7 @@ namespace tc::audio {
|
||||
std::recursive_mutex device_lock;
|
||||
std::shared_ptr<AudioDevice> device{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! */
|
||||
void* source_buffer = nullptr;
|
||||
|
@ -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<std::shared_ptr<AudioDevice>> devices() {
|
||||
std::deque<std::shared_ptr<AudioDevice>> 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<AudioDevice>& device) {
|
||||
if(device->driver() != "WASAPI") return false;
|
||||
return !ends_with(device->id(), "_raw");
|
||||
}), result.end());
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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<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);
|
||||
if(i == default_input_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);
|
||||
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 {
|
||||
|
@ -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<AudioDevicePlayback> playback() override;
|
||||
[[nodiscard]] std::shared_ptr<AudioDeviceRecord> record() override;
|
||||
private:
|
||||
std::string _device_id{};
|
||||
|
||||
std::string driver_name{};
|
||||
struct ::SoundIoDevice* device_handle{nullptr};
|
||||
bool _default{false};
|
||||
|
@ -2,29 +2,51 @@
|
||||
// Created by wolverindev on 07.02.20.
|
||||
//
|
||||
|
||||
#include "SoundIO.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#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};
|
||||
|
@ -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() {
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "ProtocolHandler.h"
|
||||
#include "ServerConnection.h"
|
||||
#include "Socket.h"
|
||||
#include "../logger.h"
|
||||
#include "Error.h"
|
||||
@ -7,7 +6,6 @@
|
||||
#include <misc/endianness.h>
|
||||
#include <protocol/buffers.h>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <tomcrypt.h>
|
||||
#include <tommath.h>
|
||||
|
||||
|
@ -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! */
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ void force_log_node(logger::category::value, spdlog::level::level_enum, const st
|
||||
#ifdef NODEJS_API
|
||||
#include <NanGet.h>
|
||||
#include <include/NanEventCallback.h>
|
||||
#include <include/NanStrings.h>
|
||||
|
||||
/* NODE JS */
|
||||
struct LogMessage {
|
||||
|
Loading…
Reference in New Issue
Block a user