diff --git a/modules/renderer/audio/AudioPlayer.ts b/modules/renderer/audio/AudioPlayer.ts index 139475d..b9d65c3 100644 --- a/modules/renderer/audio/AudioPlayer.ts +++ b/modules/renderer/audio/AudioPlayer.ts @@ -63,7 +63,9 @@ namespace audio.player { console.warn("Main audio underflow"); }; - _audioContext = new AudioContext(); + _audioContext = new AudioContext({ + sampleRate: _output_stream.sample_rate + }); _processor = _audioContext.createScriptProcessor(1024 * 8, _output_stream.channels, _output_stream.channels); _processor.onaudioprocess = function(event) { diff --git a/modules/renderer/audio/AudioRecorder.ts b/modules/renderer/audio/AudioRecorder.ts index 597f78f..ab900e0 100644 --- a/modules/renderer/audio/AudioRecorder.ts +++ b/modules/renderer/audio/AudioRecorder.ts @@ -10,6 +10,7 @@ export namespace _audio.recorder { interface NativeDevice extends InputDevice { device_index: number; + native: any; } let _device_cache: NativeDevice[] = undefined; @@ -26,6 +27,7 @@ export namespace _audio.recorder { name: e.name, driver: e.driver, sample_rate: 48000, /* TODO! */ + native: e } as NativeDevice })); } diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 41b6abb..cc20be1 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -22,11 +22,10 @@ message("Module path: ${CMAKE_MODULE_PATH}") function(setup_nodejs) set(NodeJS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") set(NODEJS_URL "https://atom.io/download/atom-shell") - set(NODEJS_VERSION "v7.1.2") - #set(NODEJS_VERSION "v8.0.0") + set(NODEJS_VERSION "v8.0.0") #set(NODEJS_URL "https://nodejs.org/download/release/") - #set(NODEJS_VERSION "v12.14.1") + #set(NODEJS_VERSION "v12.13.0") find_package(NodeJS REQUIRED) diff --git a/native/serverconnection/CMakeLists.txt b/native/serverconnection/CMakeLists.txt index dd1c316..39d326d 100644 --- a/native/serverconnection/CMakeLists.txt +++ b/native/serverconnection/CMakeLists.txt @@ -2,72 +2,89 @@ set(MODULE_NAME "teaclient_connection") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -static-libasan -lasan -lubsan") set(SOURCE_FILES - src/logger.cpp - src/EventLoop.cpp - src/hwuid.cpp + src/logger.cpp + src/EventLoop.cpp + src/hwuid.cpp src/ring_buffer.cpp - src/connection/ft/FileTransferManager.cpp - src/connection/ft/FileTransferObject.cpp + src/connection/ft/FileTransferManager.cpp + src/connection/ft/FileTransferObject.cpp - src/audio/AudioSamples.cpp - src/audio/AudioMerger.cpp - src/audio/AudioOutput.cpp - src/audio/AudioInput.cpp - src/audio/AudioResampler.cpp - src/audio/AudioReframer.cpp + src/audio/AudioSamples.cpp + src/audio/AudioMerger.cpp + src/audio/AudioOutput.cpp + src/audio/AudioInput.cpp + src/audio/AudioResampler.cpp + src/audio/AudioReframer.cpp - src/audio/filter/FilterVad.cpp - src/audio/filter/FilterThreshold.cpp - src/audio/filter/FilterState.cpp + src/audio/filter/FilterVad.cpp + src/audio/filter/FilterThreshold.cpp + src/audio/filter/FilterState.cpp - src/audio/codec/Converter.cpp - src/audio/codec/OpusConverter.cpp + src/audio/codec/Converter.cpp + src/audio/codec/OpusConverter.cpp src/audio/driver/AudioDriver.cpp - src/audio/driver/SoundIO.cpp - src/audio/driver/SoundIOPlayback.cpp - src/audio/driver/SoundIORecord.cpp -) + ) set(NODEJS_SOURCE_FILES - src/bindings.cpp + src/bindings.cpp - src/connection/ServerConnection.cpp - src/connection/Socket.cpp - src/connection/ProtocolHandler.cpp - src/connection/ProtocolHandlerPOW.cpp - src/connection/ProtocolHandlerCrypto.cpp - src/connection/ProtocolHandlerPackets.cpp - src/connection/ProtocolHandlerCommands.cpp - src/connection/audio/AudioSender.cpp - src/connection/audio/AudioEventLoop.cpp + src/connection/ServerConnection.cpp + src/connection/Socket.cpp + src/connection/ProtocolHandler.cpp + src/connection/ProtocolHandlerPOW.cpp + src/connection/ProtocolHandlerCrypto.cpp + src/connection/ProtocolHandlerPackets.cpp + src/connection/ProtocolHandlerCommands.cpp + src/connection/audio/AudioSender.cpp + src/connection/audio/AudioEventLoop.cpp - src/connection/audio/VoiceConnection.cpp - src/connection/audio/VoiceClient.cpp + src/connection/audio/VoiceConnection.cpp + src/connection/audio/VoiceClient.cpp - src/audio/js/AudioPlayer.cpp - src/audio/js/AudioOutputStream.cpp + src/audio/js/AudioPlayer.cpp + src/audio/js/AudioOutputStream.cpp - src/audio/js/AudioRecorder.cpp - src/audio/js/AudioConsumer.cpp - src/audio/js/AudioFilter.cpp + src/audio/js/AudioRecorder.cpp + src/audio/js/AudioConsumer.cpp + src/audio/js/AudioFilter.cpp ) -if (MSVC) - set(SOURCE_FILES ${SOURCE_FILES}) - add_definitions(-DUSING_UV_SHARED) +if (SOUNDIO_BACKED) + find_package(soundio REQUIRED) + include_directories(${soundio_INCLUDE_DIR}) + add_compile_definitions(SOUNDIO_STATIC_LIBRARY) + + list(APPEND SOURCE_FILES + src/audio/driver/SoundIO.cpp + src/audio/driver/SoundIOPlayback.cpp + src/audio/driver/SoundIORecord.cpp + ) + add_definitions(-DHAVE_SOUNDIO) else() - set(SOURCE_FILES ${SOURCE_FILES}) + add_definitions(-DHAVE_PORTAUDIO) + + list(APPEND SOURCE_FILES + src/audio/driver/PortAudio.cpp + src/audio/driver/PortAudioRecord.cpp + src/audio/driver/PortAudioPlayback.cpp + ) +endif() + +if (MSVC) + set(SOURCE_FILES ${SOURCE_FILES}) + add_definitions(-DUSING_UV_SHARED) +else() + set(SOURCE_FILES ${SOURCE_FILES}) endif() add_nodejs_module(${MODULE_NAME} ${SOURCE_FILES} ${NODEJS_SOURCE_FILES}) target_link_libraries(${MODULE_NAME} ${NODEJS_LIBRARIES}) #target_compile_options(${MODULE_NAME} PUBLIC "-fPIC") -find_package(soundio REQUIRED) -include_directories(${soundio_INCLUDE_DIR}) -add_compile_definitions(SOUNDIO_STATIC_LIBRARY) +find_package(PortAudio REQUIRED) +include_directories(${PortAudio_INCLUDE_DIR}) find_package(TomMath REQUIRED) include_directories(${TomMath_INCLUDE_DIR}) @@ -95,10 +112,10 @@ include_directories(${ed25519_INCLUDE_DIR}) find_package(ThreadPool REQUIRED) include_directories(${ThreadPool_INCLUDE_DIR}) if (WIN32) - add_compile_options(/NODEFAULTLIB:ThreadPoolStatic) - add_definitions(-DWINDOWS) #Required by ThreadPool - add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Let windows allow strerror - add_definitions(-D_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING) # For the FMT library + add_compile_options(/NODEFAULTLIB:ThreadPoolStatic) + add_definitions(-DWINDOWS) #Required by ThreadPool + add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Let windows allow strerror + add_definitions(-D_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING) # For the FMT library endif () find_package(Soxr REQUIRED) @@ -113,36 +130,41 @@ include_directories(${opus_INCLUDE_DIR}) find_package(spdlog REQUIRED) set(REQUIRED_LIBRARIES - ${TeaSpeak_SharedLib_LIBRARIES_STATIC} + ${TeaSpeak_SharedLib_LIBRARIES_STATIC} - ${TomCrypt_LIBRARIES_STATIC} - ${TomMath_LIBRARIES_STATIC} + ${TomCrypt_LIBRARIES_STATIC} + ${TomMath_LIBRARIES_STATIC} - ${LIBEVENT_STATIC_LIBRARIES} + ${LIBEVENT_STATIC_LIBRARIES} - ${StringVariable_LIBRARIES_STATIC} - ${DataPipes_LIBRARIES_STATIC} #Needs to be static because something causes ca bad function call when loaded in electron - ${ThreadPool_LIBRARIES_STATIC} - ${soxr_LIBRARIES_STATIC} - ${fvad_LIBRARIES_STATIC} - ${opus_LIBRARIES_STATIC} + ${StringVariable_LIBRARIES_STATIC} + ${DataPipes_LIBRARIES_STATIC} #Needs to be static because something causes ca bad function call when loaded in electron + ${ThreadPool_LIBRARIES_STATIC} + ${soxr_LIBRARIES_STATIC} + ${fvad_LIBRARIES_STATIC} + ${opus_LIBRARIES_STATIC} - ${ed25519_LIBRARIES_STATIC} - soundio::static + ${ed25519_LIBRARIES_STATIC} - spdlog::spdlog_header_only - Nan::Helpers -) + spdlog::spdlog_header_only + Nan::Helpers + ) + +if (SOUNDIO_BACKED) + list(APPEND REQUIRED_LIBRARIES soundio::static) +else() + list(APPEND REQUIRED_LIBRARIES ${PortAudio_LIBRARIES_STATIC}) +endif () if (WIN32) - set(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} "Ws2_32.Lib") + set(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} "Ws2_32.Lib") else() - set(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} - libstdc++fs.a - asound - jack.a - pthread - ) + set(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} + libstdc++fs.a + asound + jack.a + pthread + ) endif() add_definitions(-DNO_OPEN_SSL) @@ -157,5 +179,5 @@ target_link_libraries(Audio-Test-2 ${REQUIRED_LIBRARIES}) add_executable(HW-UID-Test src/hwuid.cpp) target_link_libraries(HW-UID-Test - ${REQUIRED_LIBRARIES} -) \ No newline at end of file + ${REQUIRED_LIBRARIES} + ) \ No newline at end of file diff --git a/native/serverconnection/src/audio/AudioOutput.cpp b/native/serverconnection/src/audio/AudioOutput.cpp index fc492b3..b2f7f99 100644 --- a/native/serverconnection/src/audio/AudioOutput.cpp +++ b/native/serverconnection/src/audio/AudioOutput.cpp @@ -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; } } diff --git a/native/serverconnection/src/audio/AudioOutput.h b/native/serverconnection/src/audio/AudioOutput.h index 1fa4382..385508c 100644 --- a/native/serverconnection/src/audio/AudioOutput.h +++ b/native/serverconnection/src/audio/AudioOutput.h @@ -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; diff --git a/native/serverconnection/src/audio/AudioResampler.h b/native/serverconnection/src/audio/AudioResampler.h index 72fa2e9..66023d8 100644 --- a/native/serverconnection/src/audio/AudioResampler.h +++ b/native/serverconnection/src/audio/AudioResampler.h @@ -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; }; - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/native/serverconnection/src/audio/driver/AudioDriver.cpp b/native/serverconnection/src/audio/driver/AudioDriver.cpp index 903fe4e..b443451 100644 --- a/native/serverconnection/src/audio/driver/AudioDriver.cpp +++ b/native/serverconnection/src/audio/driver/AudioDriver.cpp @@ -5,21 +5,22 @@ #include #include #include -#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> devices() { std::deque> 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 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_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) { { diff --git a/native/serverconnection/src/audio/driver/PortAudio.cpp b/native/serverconnection/src/audio/driver/PortAudio.cpp new file mode 100644 index 0000000..4d260f7 --- /dev/null +++ b/native/serverconnection/src/audio/driver/PortAudio.cpp @@ -0,0 +1,111 @@ +// +// Created by WolverinDEV on 13/02/2020. +// + +#include "PortAudio.h" +#include "../../logger.h" +#include + +namespace tc::audio::pa { + std::mutex _audio_devices_lock; + std::unique_ptr>> _audio_devices{}; + + void initialize_devices() { + std::lock_guard dev_lock{_audio_devices_lock}; + _audio_devices = std::make_unique>>(); + + /* 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(device_index, device_info, device_host_info)); + } + + } + + void initialize() { + Pa_Initialize(); + initialize_devices(); + } + + void finalize() { + Pa_Terminate(); + } + + std::deque> 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 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(this->_index, this->_info); + return this->_playback; + } + + std::shared_ptr 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(this->_index, this->_info); + return this->_record; + } +} \ No newline at end of file diff --git a/native/serverconnection/src/audio/driver/PortAudio.h b/native/serverconnection/src/audio/driver/PortAudio.h new file mode 100644 index 0000000..53c16df --- /dev/null +++ b/native/serverconnection/src/audio/driver/PortAudio.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include +#include +#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 playback() override; + [[nodiscard]] std::shared_ptr record() override; + private: + const PaDeviceIndex _index; + const PaDeviceInfo* _info; + const PaHostApiInfo* _host_info; + + std::mutex io_lock{}; + std::shared_ptr _playback; + std::shared_ptr _record; + }; + + + extern void initialize(); + extern void finalize(); + + extern std::deque> devices(); +} \ No newline at end of file diff --git a/native/serverconnection/src/audio/driver/PortAudioPlayback.cpp b/native/serverconnection/src/audio/driver/PortAudioPlayback.cpp new file mode 100644 index 0000000..22b70b7 --- /dev/null +++ b/native/serverconnection/src/audio/driver/PortAudioPlayback.cpp @@ -0,0 +1,95 @@ +// +// Created by WolverinDEV on 13/02/2020. +// + + +#include +#include +#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(userData); + assert(player); + + player->write_callback(output, frameCount, timeInfo, statusFlags); + return 0; + }; + + PaStreamParameters parameters{}; + memset(¶meters, 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, + ¶meters, + (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); +} \ No newline at end of file diff --git a/native/serverconnection/src/audio/driver/PortAudioRecord.cpp b/native/serverconnection/src/audio/driver/PortAudioRecord.cpp new file mode 100644 index 0000000..a522b71 --- /dev/null +++ b/native/serverconnection/src/audio/driver/PortAudioRecord.cpp @@ -0,0 +1,95 @@ +// +// Created by WolverinDEV on 13/02/2020. +// + + +#include +#include +#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(userData); + assert(recorder); + + recorder->read_callback(input, frameCount, timeInfo, statusFlags); + return 0; + }; + + PaStreamParameters parameters{}; + memset(¶meters, 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, + ¶meters, + 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); +} \ No newline at end of file diff --git a/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp b/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp index b21b81d..0b14f7a 100644 --- a/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp +++ b/native/serverconnection/src/audio/driver/SoundIOPlayback.cpp @@ -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; diff --git a/native/serverconnection/src/audio/js/AudioPlayer.cpp b/native/serverconnection/src/audio/js/AudioPlayer.cpp index 6100abf..90db27c 100644 --- a/native/serverconnection/src/audio/js/AudioPlayer.cpp +++ b/native/serverconnection/src/audio/js/AudioPlayer.cpp @@ -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("current_device").ToLocalChecked(), Nan::GetFunction(Nan::New(player::current_playback_device)).ToLocalChecked()); - Nan::Set(target, Nan::New("set_device").ToLocalChecked(), Nan::GetFunction(Nan::New(player::set_playback_device)).ToLocalChecked()); + Nan::Set(target, Nan::LocalString("current_device"), Nan::GetFunction(Nan::New(player::current_playback_device)).ToLocalChecked()); + Nan::Set(target, Nan::LocalString("set_device"), Nan::GetFunction(Nan::New(player::set_playback_device)).ToLocalChecked()); - Nan::Set(target, Nan::New("create_stream").ToLocalChecked(), Nan::GetFunction(Nan::New(player::create_stream)).ToLocalChecked()); + Nan::Set(target, Nan::LocalString("create_stream"), Nan::GetFunction(Nan::New(player::create_stream)).ToLocalChecked()); - Nan::Set(target, Nan::New("get_master_volume").ToLocalChecked(), Nan::GetFunction(Nan::New(player::get_master_volume)).ToLocalChecked()); - Nan::Set(target, Nan::New("set_master_volume").ToLocalChecked(), Nan::GetFunction(Nan::New(player::set_master_volume)).ToLocalChecked()); + Nan::Set(target, Nan::LocalString("get_master_volume"), Nan::GetFunction(Nan::New(player::get_master_volume)).ToLocalChecked()); + Nan::Set(target, Nan::LocalString("set_master_volume"), Nan::GetFunction(Nan::New(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("driver").ToLocalChecked(), Nan::New(device->driver()).ToLocalChecked()); - Nan::Set(device_info, Nan::New("device_id").ToLocalChecked(), Nan::New(device->id()).ToLocalChecked()); + Nan::Set(device_info, Nan::LocalString("driver"), Nan::LocalStringUTF8(device->driver())); + Nan::Set(device_info, Nan::LocalString("device_id"), Nan::New(device->id()).ToLocalChecked()); - Nan::Set(device_info, Nan::New("input_supported").ToLocalChecked(), Nan::New(device->is_input_supported())); - Nan::Set(device_info, Nan::New("output_supported").ToLocalChecked(), Nan::New(device->is_output_supported())); + Nan::Set(device_info, Nan::LocalString("input_supported"), Nan::New(device->is_input_supported())); + Nan::Set(device_info, Nan::LocalString("output_supported"), Nan::New(device->is_output_supported())); - Nan::Set(device_info, Nan::New("input_default").ToLocalChecked(), Nan::New(device->is_input_default())); - Nan::Set(device_info, Nan::New("output_default").ToLocalChecked(), Nan::New(device->is_output_default())); + Nan::Set(device_info, Nan::LocalString("input_default"), Nan::New(device->is_input_default())); + Nan::Set(device_info, Nan::LocalString("output_default"), Nan::New(device->is_output_default())); Nan::Set(result, index, device_info); } diff --git a/native/serverconnection/src/bindings.cpp b/native/serverconnection/src/bindings.cpp index 0a38c8b..3f0a232 100644 --- a/native/serverconnection/src/bindings.cpp +++ b/native/serverconnection/src/bindings.cpp @@ -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); diff --git a/native/serverconnection/src/hwuid.cpp b/native/serverconnection/src/hwuid.cpp index 90e91bc..1803641 100644 --- a/native/serverconnection/src/hwuid.cpp +++ b/native/serverconnection/src/hwuid.cpp @@ -1,8 +1,6 @@ #include "./hwuid.h" #include -#include #include -#include #include #ifndef WIN32 diff --git a/native/serverconnection/src/ring_buffer.cpp b/native/serverconnection/src/ring_buffer.cpp index 1726b9f..ea9e4d3 100644 --- a/native/serverconnection/src/ring_buffer.cpp +++ b/native/serverconnection/src/ring_buffer.cpp @@ -3,13 +3,13 @@ // #include "ring_buffer.h" + +#ifdef HAVE_SOUNDIO +using namespace tc; #include - -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); -} \ No newline at end of file +} +#else + +#include +#include +#include + +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 \ No newline at end of file diff --git a/native/serverconnection/src/ring_buffer.h b/native/serverconnection/src/ring_buffer.h index 6a357e0..f0230ff 100644 --- a/native/serverconnection/src/ring_buffer.h +++ b/native/serverconnection/src/ring_buffer.h @@ -1,6 +1,7 @@ #pragma once #include +#include 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}; }; } \ No newline at end of file diff --git a/native/serverconnection/test/js/audio.ts b/native/serverconnection/test/js/audio.ts index 1e0227e..1d00fea 100644 --- a/native/serverconnection/test/js/audio.ts +++ b/native/serverconnection/test/js/audio.ts @@ -1,10 +1,10 @@ /// console.log("Starting app"); module.paths.push("../../build/linux_x64"); -module.paths.push("../../build/win32_64"); +module.paths.push("../../build/win32_x64"); const original_require = require; -require = (module => original_require(__dirname + "/../../../build/linux_x64/" + module + ".node")) as any; +require = (module => original_require(__dirname + "/../../../build/win32_x64/" + module + ".node")) as any; import * as handle from "teaclient_connection"; require = original_require; diff --git a/package-lock.json b/package-lock.json index 8394c64..182ddcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "TeaClient", - "version": "1.4.2-5", + "version": "1.4.3", "lockfileVersion": 1, "requires": true, "dependencies": { "@electron/get": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.7.2.tgz", - "integrity": "sha512-LSE4LZGMjGS9TloDx0yO44D2UTbaeKRk+QjlhWLiQlikV6J4spgDCjb6z4YIcqmPAwNzlNCnWF4dubytwI+ATA==", + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.7.6.tgz", + "integrity": "sha512-zlNikt6ziVLNcm4lly1L4y62fJd/eYpEBjF5DiV/VAQq2vdPjH4sbUphXt9upmHz86lAhAj8g9lTnWrxJ/KBZw==", "requires": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -108,8 +108,7 @@ "@types/node": { "version": "12.12.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.6.tgz", - "integrity": "sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==", - "dev": true + "integrity": "sha512-FjsYUPzEJdGXjwKqSpE0/9QEh6kzhTAeObA54rn6j3rR4C/mzpI9L0KNfoeASSPMMdxIsoJuCLDWcM/rVjIsSA==" }, "@types/request": { "version": "2.48.4", @@ -153,19 +152,12 @@ "dev": true }, "@types/ssh2": { - "version": "0.5.39", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.39.tgz", - "integrity": "sha512-MtX4tr6Jtdn/JPVsQytjB/NBeOd7JfCyf/l79KOdkUYL+r4GXUXcySX1n8W4O61fnQdljTBXEPJJ2dhnGhi2xg==", + "version": "0.5.40", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.40.tgz", + "integrity": "sha512-Yrs2vFIirdscR+xY4leiUosHTClRbVBiFLlm5gA0JMZMjbCulHKO3qcgMqATElrquiZal5sSHoXMk4t8DzDawA==", "requires": { "@types/node": "*", "@types/ssh2-streams": "*" - }, - "dependencies": { - "@types/node": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", - "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==" - } } }, "@types/ssh2-streams": { @@ -174,13 +166,6 @@ "integrity": "sha512-NB+SYftagfHTDPdgVyvSZCeg5MURyHECd/PycGIW9hwhnEbTFxIdDbFtf3jxUO3rRnfr0lfdtkZFiv28T1cAsg==", "requires": { "@types/node": "*" - }, - "dependencies": { - "@types/node": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", - "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==" - } } }, "@types/tar-stream": { @@ -552,9 +537,9 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "boolean": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.0.tgz", - "integrity": "sha512-OElxJ1lUSinuoUnkpOgLmxp0DC4ytEhODEL6QJU0NpxE/mI4rUSh8h1P1Wkvfi3xQEBcxXR2gBIPNYNuaFcAbQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", + "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", "optional": true }, "boxen": { @@ -1431,20 +1416,13 @@ "dev": true }, "electron": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-7.1.2.tgz", - "integrity": "sha512-7hjONYt2GlQfKuKgQrhhUL1P9lbGWLBfMUq+2QFU3yeLtCvM0ROfPJCRP4OF5pVp3KDyfFp4DtmhuVzAnxV3jA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-8.0.0.tgz", + "integrity": "sha512-vBXUKRqTUq0jv1upvISdvScDDH3uCPwXj4eA5BeR3UDbJp2hOhq7eJxwjIQbfLQql98aYz4X6pSlzBnhfyQqHA==", "requires": { "@electron/get": "^1.0.1", "@types/node": "^12.0.12", "extract-zip": "^1.0.3" - }, - "dependencies": { - "@types/node": { - "version": "12.12.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.26.tgz", - "integrity": "sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA==" - } } }, "electron-context-menu": { @@ -3530,9 +3508,9 @@ "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" }, "http-cache-semantics": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", - "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-Z2EICWNJou7Tr9Bd2M2UqDJq3A9F2ePG9w3lIpjoyuSyXFP9QbniJVu3XQYytuw5ebmG7dXSXO9PgAjJG8DDKA==" }, "http-signature": { "version": "1.3.1", @@ -5293,9 +5271,9 @@ } }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -5304,7 +5282,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -5314,7 +5292,7 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, @@ -5345,18 +5323,13 @@ "verror": "1.10.0" } }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "^1.1.28", + "punycode": "^2.1.1" } } } @@ -5561,9 +5534,9 @@ } }, "semver": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.2.tgz", - "integrity": "sha512-BJs9T/H8sEVHbeigqzIEo57Iu/3DG6c4QoqTfbQB3BPA4zgzAomh/Fk9E7QtjWQ8mx2dgA9YCfSF4y9k9bHNpQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", "optional": true }, "semver-compare": { diff --git a/package.json b/package.json index f1d3438..a5dffd0 100644 --- a/package.json +++ b/package.json @@ -38,26 +38,26 @@ "asar": "^2.0.1", "cmake-js": "^4.0.1", "ejs": "^2.7.1", - "electron-installer-windows": "^1.1.1", + "electron-installer-windows": "^1.1.0", "electron-packager": "8.7.2", "electron-winstaller": "^2.7.0", - "electron-wix-msi": "^2.2.0", + "electron-wix-msi": "^2.1.1", "nodemon": "^1.19.4", "platform-dependent-modules": "0.0.14", "rc": "^1.2.8", - "rcedit": "^1.1.2", + "rcedit": "^1.1.1", "sass": "^1.23.2", "typescript": "^3.7.2" }, "dependencies": { "@types/minimist": "^1.2.0", - "@types/ssh2": "^0.5.39", + "@types/ssh2": "^0.5.40", "argparse": "^1.0.10", "asn1": "^0.2.4", "assert-plus": "^1.0.0", "aws-sign2": "^0.7.0", "aws4": "^1.8.0", - "electron": "7.1.2", + "electron": "8.0.0", "electron-navigation": "^1.5.8", "electron-rebuild": "^1.8.6", "extend": "^3.0.2", @@ -73,7 +73,7 @@ "only": "0.0.2", "psl": "^1.4.0", "pure-uuid": "^1.5.7", - "request": "^2.47.1", + "request": "^2.88.2", "request-progress": "^3.0.0", "request-promise": "^4.2.5", "safe-buffer": "^5.2.0",