Revered the audio system back to PortAudio

This commit is contained in:
WolverinDEV 2020-02-13 22:16:34 +01:00
parent 91982ecc7b
commit 19b7e46137
21 changed files with 778 additions and 184 deletions

View File

@ -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) {

View File

@ -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
}));
}

View File

@ -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)

View File

@ -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}
)
${REQUIRED_LIBRARIES}
)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
};
}
}
}

View File

@ -5,21 +5,22 @@
#include <thread>
#include <condition_variable>
#include <ThreadPool/ThreadHelper.h>
#include "AudioDriver.h"
#include "SoundIO.h"
#include "./AudioDriver.h"
#include "../../logger.h"
#include "../AudioMerger.h"
#ifdef HAVE_SOUNDIO
#include "./SoundIO.h"
#else
#include "PortAudio.h"
#endif
using namespace tc::audio;
namespace tc::audio {
inline bool ends_with(std::string const & value, std::string const & ending) {
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
std::deque<std::shared_ptr<AudioDevice>> devices() {
std::deque<std::shared_ptr<AudioDevice>> result{};
#ifdef HAVE_SOUNDIO
for(auto& backend : SoundIOBackendHandler::all_backends()) {
auto input_devices = backend->input_devices();
auto output_devices = backend->output_devices();
@ -27,16 +28,26 @@ namespace tc::audio {
result.insert(result.end(), input_devices.begin(), input_devices.end());
result.insert(result.end(), output_devices.begin(), output_devices.end());
}
#else
auto devices = pa::devices();
result.insert(result.end(), devices.begin(), devices.end());
#endif
return result;
}
std::shared_ptr<AudioDevice> find_device_by_id(const std::string_view& id, bool input) {
#ifdef HAVE_SOUNDIO
for(auto& backend : SoundIOBackendHandler::all_backends()) {
for(auto& dev : input ? backend->input_devices() : backend->output_devices())
if(dev->id() == id)
return dev;
}
#else
for(auto& device : devices())
if(device->id() == id && (input ? device->is_input_supported() : device->is_output_supported()))
return device;
#endif
return nullptr;
}
@ -44,6 +55,7 @@ namespace tc::audio {
std::deque<initialize_callback_t> initialize_callbacks{};
int initialize_state{0}; /* 0 := not initialized | 1 := initialized | 2 := initializing */
#ifdef HAVE_SOUNDIO
void _initialize() {
SoundIOBackendHandler::initialize_all();
SoundIOBackendHandler::connect_all();
@ -52,6 +64,15 @@ namespace tc::audio {
void _finalize() {
SoundIOBackendHandler::shutdown_all();
}
#else
void _initialize() {
pa::initialize();
}
void _finalize() {
pa::finalize();
}
#endif
void initialize(const initialize_callback_t& callback) {
{

View File

@ -0,0 +1,111 @@
//
// Created by WolverinDEV on 13/02/2020.
//
#include "PortAudio.h"
#include "../../logger.h"
#include <portaudio.h>
namespace tc::audio::pa {
std::mutex _audio_devices_lock;
std::unique_ptr<std::deque<std::shared_ptr<PaAudioDevice>>> _audio_devices{};
void initialize_devices() {
std::lock_guard dev_lock{_audio_devices_lock};
_audio_devices = std::make_unique<std::deque<std::shared_ptr<PaAudioDevice>>>();
/* query devices */
auto device_count = Pa_GetDeviceCount();
if(device_count < 0) {
log_error(category::audio, tr("Pa_GetDeviceCount() returned {}"), device_count);
return;
}
for(PaDeviceIndex device_index = 0; device_index < device_count; device_index++) {
auto device_info = Pa_GetDeviceInfo(device_index);
if(!device_info) {
log_warn(category::audio, tr("Pa_GetDeviceInfo(...) failed for device {}"), device_index);
continue;
}
auto device_host_info = Pa_GetHostApiInfo(device_info->hostApi);
if(!device_host_info) {
log_warn(category::audio, tr("Pa_GetHostApiInfo(...) failed for device {} with host api {}"), device_index, device_info->hostApi);
continue;
}
_audio_devices->push_back(std::make_shared<PaAudioDevice>(device_index, device_info, device_host_info));
}
}
void initialize() {
Pa_Initialize();
initialize_devices();
}
void finalize() {
Pa_Terminate();
}
std::deque<std::shared_ptr<PaAudioDevice>> devices() {
std::lock_guard dev_lock{_audio_devices_lock};
return *_audio_devices;
}
/* device class */
PaAudioDevice::PaAudioDevice(PaDeviceIndex index, const PaDeviceInfo* info, const PaHostApiInfo* host)
: _index{index}, _info{info}, _host_info{host} { }
std::string PaAudioDevice::id() const {
return std::string{this->_info->name} + "_" + this->_host_info->name;
}
std::string PaAudioDevice::name() const {
return this->_info->name;
}
std::string PaAudioDevice::driver() const {
return this->_host_info->name;
}
bool PaAudioDevice::is_input_supported() const {
return this->_info->maxInputChannels > 0;
}
bool PaAudioDevice::is_output_supported() const {
return this->_info->maxOutputChannels > 0;
}
bool PaAudioDevice::is_input_default() const {
return this->_index == Pa_GetDefaultInputDevice();
}
bool PaAudioDevice::is_output_default() const {
return this->_index == Pa_GetDefaultOutputDevice();
}
std::shared_ptr<AudioDevicePlayback> PaAudioDevice::playback() {
if(!this->is_output_supported()) {
log_warn(category::audio, tr("Tried to create playback manager for device which does not supports it."));
return nullptr;
}
std::lock_guard lock{this->io_lock};
if(!this->_playback)
this->_playback = std::make_shared<PortAudioPlayback>(this->_index, this->_info);
return this->_playback;
}
std::shared_ptr<AudioDeviceRecord> PaAudioDevice::record() {
if(!this->is_input_supported()) {
log_warn(category::audio, tr("Tried to create record manager for device which does not supports it."));
return nullptr;
}
std::lock_guard lock{this->io_lock};
if(!this->_record)
this->_record = std::make_shared<PortAudioRecord>(this->_index, this->_info);
return this->_record;
}
}

View File

@ -0,0 +1,87 @@
#pragma once
#include <memory>
#include <array>
#include <vector>
#include <mutex>
#include <portaudio.h>
#include "./AudioDriver.h"
namespace tc::audio::pa {
class PortAudioPlayback : public AudioDevicePlayback {
public:
static constexpr auto kChannelCount{2};
static constexpr auto kSampleRate{44100};
static constexpr auto kTimeSpan{0.01};
explicit PortAudioPlayback(PaDeviceIndex index, const PaDeviceInfo* info);
virtual ~PortAudioPlayback();
[[nodiscard]] size_t sample_rate() const override;
protected:
bool impl_start(std::string& /* error */) override;
void impl_stop() override;
private:
void write_callback(void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags);
PaDeviceIndex index;
const PaDeviceInfo* info;
PaStream* stream{nullptr};
};
class PortAudioRecord : public AudioDeviceRecord {
public:
static constexpr auto kChannelCount{2};
static constexpr auto kSampleRate{44100};
explicit PortAudioRecord(PaDeviceIndex index, const PaDeviceInfo* info);
virtual ~PortAudioRecord();
[[nodiscard]] size_t sample_rate() const override;
protected:
bool impl_start(std::string& /* error */) override;
void impl_stop() override;
private:
void read_callback(const void *input, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags);
PaDeviceIndex index;
const PaDeviceInfo* info;
PaStream* stream{nullptr};
};
struct PaAudioDevice : public AudioDevice {
public:
explicit PaAudioDevice(PaDeviceIndex index, const PaDeviceInfo* info, const PaHostApiInfo* host);
virtual ~PaAudioDevice() = default;
[[nodiscard]] std::string id() const override;
[[nodiscard]] std::string name() const override;
[[nodiscard]] std::string driver() const override;
[[nodiscard]] bool is_input_supported() const override;
[[nodiscard]] bool is_output_supported() const override;
[[nodiscard]] bool is_input_default() const override;
[[nodiscard]] bool is_output_default() const override;
[[nodiscard]] std::shared_ptr<AudioDevicePlayback> playback() override;
[[nodiscard]] std::shared_ptr<AudioDeviceRecord> record() override;
private:
const PaDeviceIndex _index;
const PaDeviceInfo* _info;
const PaHostApiInfo* _host_info;
std::mutex io_lock{};
std::shared_ptr<PortAudioPlayback> _playback;
std::shared_ptr<PortAudioRecord> _record;
};
extern void initialize();
extern void finalize();
extern std::deque<std::shared_ptr<PaAudioDevice>> devices();
}

View File

@ -0,0 +1,95 @@
//
// Created by WolverinDEV on 13/02/2020.
//
#include <algorithm>
#include <cmath>
#include "./PortAudio.h"
#include "../../logger.h"
using namespace tc::audio::pa;
PortAudioPlayback::PortAudioPlayback(PaDeviceIndex index, const PaDeviceInfo *info)
: index{index}, info{info} { }
PortAudioPlayback::~PortAudioPlayback() {
this->stop();
}
bool PortAudioPlayback::impl_start(std::string &error) {
//TODO: Detect correct sample rate
{
auto device_info = Pa_GetDeviceInfo(this->index);
if(this->info != device_info) {
error = "invalid info pointer";
return false;
}
}
const auto proxied_write_callback = [](
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData) {
assert(output);
auto player = reinterpret_cast<PortAudioPlayback*>(userData);
assert(player);
player->write_callback(output, frameCount, timeInfo, statusFlags);
return 0;
};
PaStreamParameters parameters{};
memset(&parameters, 0, sizeof(parameters));
parameters.channelCount = (int) kChannelCount;
parameters.device = this->index;
parameters.sampleFormat = paFloat32;
parameters.suggestedLatency = this->info->defaultLowInputLatency;
auto err = Pa_OpenStream(
&this->stream,
nullptr,
&parameters,
(double) kSampleRate,
kSampleRate * kTimeSpan,
paClipOff,
proxied_write_callback,
this);
if(err != paNoError) {
this->stream = nullptr;
error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")";
return false;
}
err = Pa_StartStream(this->stream);
if(err != paNoError) {
error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")";
err = Pa_CloseStream(this->stream);
if(err != paNoError)
log_critical(category::audio, tr("Failed to close opened pa stream. This will cause memory leaks. Error: {}/{}"), err, Pa_GetErrorText(err));
return false;
}
return true;
}
void PortAudioPlayback::impl_stop() {
if(Pa_IsStreamActive(this->stream))
Pa_AbortStream(this->stream);
auto error = Pa_CloseStream(this->stream);
if(error != paNoError)
log_error(category::audio, tr("Failed to close PA stream: {}"), error);
this->stream = nullptr;
}
size_t PortAudioPlayback::sample_rate() const {
return this->info->defaultSampleRate;
}
void PortAudioPlayback::write_callback(void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
this->fill_buffer(output, frameCount, kChannelCount);
}

View File

@ -0,0 +1,95 @@
//
// Created by WolverinDEV on 13/02/2020.
//
#include <algorithm>
#include <cmath>
#include "./PortAudio.h"
#include "../../logger.h"
using namespace tc::audio::pa;
PortAudioRecord::PortAudioRecord(PaDeviceIndex index, const PaDeviceInfo *info) : index{index}, info{info} {}
PortAudioRecord::~PortAudioRecord() {
this->stop();
}
bool PortAudioRecord::impl_start(std::string &error) {
//TODO: Detect correct sample rate
{
auto device_info = Pa_GetDeviceInfo(this->index);
if(this->info != device_info) {
error = "invalid info pointer";
return false;
}
}
const auto proxied_read_callback = [](
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData) {
assert(input);
auto recorder = reinterpret_cast<PortAudioRecord*>(userData);
assert(recorder);
recorder->read_callback(input, frameCount, timeInfo, statusFlags);
return 0;
};
PaStreamParameters parameters{};
memset(&parameters, 0, sizeof(parameters));
parameters.channelCount = (int) kChannelCount;
parameters.device = this->index;
parameters.sampleFormat = paFloat32;
parameters.suggestedLatency = this->info->defaultLowInputLatency;
auto err = Pa_OpenStream(
&this->stream,
&parameters,
nullptr,
(double) kSampleRate,
paFramesPerBufferUnspecified,
paClipOff,
proxied_read_callback,
this);
if(err != paNoError) {
this->stream = nullptr;
error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")";
return false;
}
err = Pa_StartStream(this->stream);
if(err != paNoError) {
error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")";
err = Pa_CloseStream(this->stream);
if(err != paNoError)
log_critical(category::audio, tr("Failed to close opened pa stream. This will cause memory leaks. Error: {}/{}"), err, Pa_GetErrorText(err));
return false;
}
return true;
}
void PortAudioRecord::impl_stop() {
if(Pa_IsStreamActive(this->stream))
Pa_AbortStream(this->stream);
auto error = Pa_CloseStream(this->stream);
if(error != paNoError)
log_error(category::audio, tr("Failed to close PA stream: {}"), error);
this->stream = nullptr;
}
size_t PortAudioRecord::sample_rate() const {
return this->info->defaultSampleRate;
}
void PortAudioRecord::read_callback(const void *input, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
std::lock_guard consumer_lock{this->consumer_lock};
for(auto& consumer : this->_consumers)
consumer->consume(input, frameCount, kChannelCount);
}

View File

@ -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;

View File

@ -11,13 +11,13 @@ using namespace tc::audio;
extern tc::audio::AudioOutput* global_audio_output;
NAN_MODULE_INIT(player::init_js) {
Nan::Set(target, Nan::New<v8::String>("current_device").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::current_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("set_device").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("current_device"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::current_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("set_device"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_playback_device)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("create_stream").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::create_stream)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("create_stream"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::create_stream)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("get_master_volume").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::get_master_volume)).ToLocalChecked());
Nan::Set(target, Nan::New<v8::String>("set_master_volume").ToLocalChecked(), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_master_volume)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("get_master_volume"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::get_master_volume)).ToLocalChecked());
Nan::Set(target, Nan::LocalString("set_master_volume"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(player::set_master_volume)).ToLocalChecked());
}
NAN_METHOD(audio::available_devices) {
@ -34,14 +34,14 @@ NAN_METHOD(audio::available_devices) {
auto device = devices[index];
Nan::Set(device_info, Nan::LocalString("name"), Nan::LocalStringUTF8(device->name()));
Nan::Set(device_info, Nan::New<v8::String>("driver").ToLocalChecked(), Nan::New<v8::String>(device->driver()).ToLocalChecked());
Nan::Set(device_info, Nan::New<v8::String>("device_id").ToLocalChecked(), Nan::New<v8::String>(device->id()).ToLocalChecked());
Nan::Set(device_info, Nan::LocalString("driver"), Nan::LocalStringUTF8(device->driver()));
Nan::Set(device_info, Nan::LocalString("device_id"), Nan::New<v8::String>(device->id()).ToLocalChecked());
Nan::Set(device_info, Nan::New<v8::String>("input_supported").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_input_supported()));
Nan::Set(device_info, Nan::New<v8::String>("output_supported").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_output_supported()));
Nan::Set(device_info, Nan::LocalString("input_supported"), Nan::New<v8::Boolean>(device->is_input_supported()));
Nan::Set(device_info, Nan::LocalString("output_supported"), Nan::New<v8::Boolean>(device->is_output_supported()));
Nan::Set(device_info, Nan::New<v8::String>("input_default").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_input_default()));
Nan::Set(device_info, Nan::New<v8::String>("output_default").ToLocalChecked(), Nan::New<v8::Boolean>(device->is_output_default()));
Nan::Set(device_info, Nan::LocalString("input_default"), Nan::New<v8::Boolean>(device->is_input_default()));
Nan::Set(device_info, Nan::LocalString("output_default"), Nan::New<v8::Boolean>(device->is_output_default()));
Nan::Set(result, index, device_info);
}

View File

@ -150,7 +150,7 @@ NAN_MODULE_INIT(init) {
}
/* TODO: Test error codes and make the audi playback device configurable */
global_audio_output = new tc::audio::AudioOutput(2, 48000);
global_audio_output = new tc::audio::AudioOutput(2, 44100);
global_audio_output->set_device(default_output);
if(!global_audio_output->playback(error)) {
logger::error(category::audio, "Failed to start audio playback: {}", error);

View File

@ -1,8 +1,6 @@
#include "./hwuid.h"
#include <tomcrypt.h>
#include <array>
#include <fstream>
#include <misc/strobf.h>
#include <misc/base64.h>
#ifndef WIN32

View File

@ -3,13 +3,13 @@
//
#include "ring_buffer.h"
#ifdef HAVE_SOUNDIO
using namespace tc;
#include <soundio/soundio.h>
using namespace tc;
ring_buffer::ring_buffer(size_t cap) {
this->handle = soundio_ring_buffer_create(nullptr, cap);
this->handle = soundio_ring_buffer_create(nullptr, (int) cap);
}
ring_buffer::~ring_buffer() {
@ -33,7 +33,7 @@ char* ring_buffer::write_ptr() {
}
void ring_buffer::advance_write_ptr(size_t bytes) {
soundio_ring_buffer_advance_write_ptr((SoundIoRingBuffer*) this->handle, bytes);
soundio_ring_buffer_advance_write_ptr((SoundIoRingBuffer*) this->handle, (int) bytes);
}
const void* ring_buffer::read_ptr() const {
@ -41,9 +41,172 @@ const void* ring_buffer::read_ptr() const {
}
void ring_buffer::advance_read_ptr(size_t bytes) {
soundio_ring_buffer_advance_read_ptr((SoundIoRingBuffer*) this->handle, bytes);
soundio_ring_buffer_advance_read_ptr((SoundIoRingBuffer*) this->handle, (int) bytes);
}
void ring_buffer::clear() {
soundio_ring_buffer_clear((SoundIoRingBuffer*) this->handle);
}
}
#else
#include <windows.h>
#include <objbase.h>
#include <cassert>
namespace tc {
bool sysinfo_initialized{false};
static SYSTEM_INFO win32_system_info;
int soundio_os_page_size() {
if(!sysinfo_initialized) {
GetSystemInfo(&win32_system_info);
sysinfo_initialized = true;
}
return win32_system_info.dwAllocationGranularity;
}
static inline size_t ceil_dbl_to_size_t(double x) {
const double truncation = (size_t)x;
return truncation + (truncation < x);
}
ring_buffer::ring_buffer(size_t min_capacity) {
this->allocate_memory(min_capacity);
}
ring_buffer::~ring_buffer() {
this->free_memory();
}
bool ring_buffer::allocate_memory(size_t requested_capacity) {
size_t actual_capacity = ceil_dbl_to_size_t(requested_capacity / (double) soundio_os_page_size()) * soundio_os_page_size();
this->memory.address = nullptr;
#ifdef WIN32
BOOL ok;
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, actual_capacity * 2, nullptr);
if (!hMapFile) return false;
for (;;) {
// find a free address space with the correct size
char *address = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity * 2);
if (!address) {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
// found a big enough address space. hopefully it will remain free
// while we map to it. if not, we'll try again.
ok = UnmapViewOfFile(address);
assert(ok);
char *addr1 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity, address);
if (addr1 != address) {
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
}
char *addr2 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_WRITE, 0, 0,
actual_capacity, address + actual_capacity);
if (addr2 != address + actual_capacity) {
ok = UnmapViewOfFile(addr1);
assert(ok);
DWORD err = GetLastError();
if (err == ERROR_INVALID_ADDRESS) {
continue;
} else {
ok = CloseHandle(hMapFile);
assert(ok);
return false;
}
}
this->memory.priv = hMapFile;
this->memory.address = address;
break;
}
#else
#error "Implement me!"
#endif
this->memory.capacity = actual_capacity;
this->_capacity = actual_capacity;
return true;
}
void ring_buffer::free_memory() {
if(!this->memory.address) return;
#ifdef WIN32
BOOL ok;
ok = UnmapViewOfFile(this->memory.address);
assert(ok);
ok = UnmapViewOfFile(this->memory.address + this->memory.capacity);
assert(ok);
ok = CloseHandle((HANDLE) this->memory.priv);
assert(ok);
#else
#error "Implement me!"
#endif
this->memory.address = nullptr;
}
size_t ring_buffer::capacity() const {
return this->_capacity;
}
char* ring_buffer::write_ptr() {
auto offset{this->write_offset.load()};
return this->memory.address + (offset % this->memory.capacity);
}
void ring_buffer::advance_write_ptr(size_t bytes) {
this->write_offset.fetch_add(bytes);
assert(this->fill_count() >= 0);
}
const void* ring_buffer::read_ptr() const {
auto offset{this->read_offset.load()};
return this->memory.address + (offset % this->memory.capacity);
}
void ring_buffer::advance_read_ptr(size_t bytes) {
this->read_offset.fetch_add(bytes);
assert(this->fill_count() >= 0);
}
size_t ring_buffer::fill_count() const {
// Whichever offset we load first might have a smaller value. So we load
// the read_offset first.
auto roffset{this->read_offset.load()};
auto woffset{this->write_offset.load()};
int count = woffset - roffset;
assert(count >= 0);
assert(count <= this->memory.capacity);
return count;
}
size_t ring_buffer::free_count() const {
return this->memory.capacity - this->fill_count();
}
void ring_buffer::clear() {
this->write_offset.store(this->read_offset.load());
}
bool ring_buffer::valid() const {
return this->memory.address != nullptr;
}
}
#endif

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstddef>
#include <atomic>
namespace tc {
class ring_buffer {
@ -22,7 +23,27 @@ namespace tc {
void advance_read_ptr(size_t /* count */);
void clear();
[[nodiscard]] bool valid() const;
[[nodiscard]] inline operator bool() const { return this->valid(); }
private:
#ifndef HAVE_SOUNDIO
struct MirroredMemory {
size_t capacity;
char *address;
void *priv;
};
bool allocate_memory(size_t requested_capacity);
void free_memory();
MirroredMemory memory{};
std::atomic_long write_offset;
std::atomic_long read_offset;
size_t _capacity{0}; /* for faster access */
#endif
void* handle{nullptr};
};
}

View File

@ -1,10 +1,10 @@
/// <reference path="../../exports/exports.d.ts" />
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;

87
package-lock.json generated
View File

@ -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": {

View File

@ -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",