Improved the mouse binding system
This commit is contained in:
parent
185f70182a
commit
383c9fbda9
@ -122,5 +122,5 @@ function deploy_client() {
|
|||||||
#install_npm
|
#install_npm
|
||||||
#compile_scripts
|
#compile_scripts
|
||||||
#compile_native
|
#compile_native
|
||||||
#package_client
|
package_client
|
||||||
deploy_client
|
deploy_client
|
||||||
|
@ -85,7 +85,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
console.log(tr("Local voice ended"));
|
console.log(tr("Local voice ended"));
|
||||||
//TODO
|
//TODO Send end? (Or is this already an automated thing?)
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_voice_started() {
|
private on_voice_started() {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include <v8.h>
|
#include <v8.h>
|
||||||
#include <nan.h>
|
#include <nan.h>
|
||||||
#include <node.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <include/NanStrings.h>
|
#include <include/NanStrings.h>
|
||||||
|
@ -52,6 +52,7 @@ class KeyboardHook {
|
|||||||
inline bool attached() { return this->_attached; }
|
inline bool attached() { return this->_attached; }
|
||||||
void detach();
|
void detach();
|
||||||
|
|
||||||
|
void trigger_key_event(const enum KeyEvent::type&, const std::string& /* key */);
|
||||||
callback_event_t callback_event;
|
callback_event_t callback_event;
|
||||||
private:
|
private:
|
||||||
#ifdef HAVE_X11
|
#ifdef HAVE_X11
|
||||||
@ -71,8 +72,6 @@ class KeyboardHook {
|
|||||||
bool mouse_hook_callback(int, WPARAM, LPARAM);
|
bool mouse_hook_callback(int, WPARAM, LPARAM);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void trigger_key_event(const enum KeyEvent::type&, const std::string& /* key */);
|
|
||||||
|
|
||||||
std::map<KeyID, bool> map_key;
|
std::map<KeyID, bool> map_key;
|
||||||
std::map<KeyID, KeyID> map_special;
|
std::map<KeyID, KeyID> map_special;
|
||||||
|
|
||||||
|
@ -1,15 +1,103 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
#include "KeyboardHook.h"
|
#include "KeyboardHook.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
typedef KBDLLHOOKSTRUCT KeyboardHookStruct;
|
typedef KBDLLHOOKSTRUCT KeyboardHookStruct;
|
||||||
typedef MSLLHOOKSTRUCT MouseHookStruct;
|
typedef MSLLHOOKSTRUCT MouseHookStruct;
|
||||||
std::map<thread::id, KeyboardHook*> hook_handles;
|
thread_local KeyboardHook* thread_hook{nullptr};
|
||||||
|
|
||||||
|
struct MouseButtonEventEntry {
|
||||||
|
MouseButtonEventEntry* next;
|
||||||
|
KeyboardHook* hook;
|
||||||
|
|
||||||
|
enum KeyboardHook::KeyEvent::type type;
|
||||||
|
std::string key;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline MouseButtonEventEntry* allocate_mb_event() {
|
||||||
|
return new MouseButtonEventEntry{};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void delete_mb_event(MouseButtonEventEntry* event) {
|
||||||
|
delete event;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MouseButtonEventDispatcher {
|
||||||
|
bool active{true};
|
||||||
|
|
||||||
|
std::thread dispatcher{};
|
||||||
|
|
||||||
|
CRITICAL_SECTION mutex;
|
||||||
|
CONDITION_VARIABLE cv_flushed;
|
||||||
|
CONDITION_VARIABLE cv_work;
|
||||||
|
|
||||||
|
MouseButtonEventEntry* event_head{nullptr};
|
||||||
|
MouseButtonEventEntry** event_tail{&event_head};
|
||||||
|
};
|
||||||
|
|
||||||
|
MouseButtonEventDispatcher* global_event_dispatcher{};
|
||||||
|
size_t global_ed_ref_count{0};
|
||||||
|
|
||||||
|
void init_global_ed() {
|
||||||
|
if(global_event_dispatcher) {
|
||||||
|
global_ed_ref_count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_event_dispatcher = new MouseButtonEventDispatcher{};
|
||||||
|
InitializeCriticalSection(&global_event_dispatcher->mutex);
|
||||||
|
InitializeConditionVariable(&global_event_dispatcher->cv_flushed);
|
||||||
|
InitializeConditionVariable(&global_event_dispatcher->cv_work);
|
||||||
|
|
||||||
|
global_event_dispatcher->dispatcher = std::thread([]{
|
||||||
|
auto ed = global_event_dispatcher;
|
||||||
|
|
||||||
|
while(ed->active) {
|
||||||
|
MouseButtonEventEntry* entry{nullptr};
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&ed->mutex);
|
||||||
|
while(!global_event_dispatcher->event_head && ed->active) {
|
||||||
|
WakeAllConditionVariable(&ed->cv_flushed);
|
||||||
|
SleepConditionVariableCS(&ed->cv_work, &ed->mutex, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = global_event_dispatcher->event_head;
|
||||||
|
global_event_dispatcher->event_head = nullptr;
|
||||||
|
global_event_dispatcher->event_tail = &global_event_dispatcher->event_head;
|
||||||
|
LeaveCriticalSection(&ed->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(entry) {
|
||||||
|
entry->hook->trigger_key_event(entry->type, std::string{entry->key});
|
||||||
|
|
||||||
|
auto next = entry->next;
|
||||||
|
delete_mb_event(entry);
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown_global_ed() {
|
||||||
|
if(--global_ed_ref_count > 0) return;
|
||||||
|
|
||||||
|
auto ed = std::exchange(global_event_dispatcher, nullptr);
|
||||||
|
ed->active = false;
|
||||||
|
WakeAllConditionVariable(&ed->cv_work);
|
||||||
|
|
||||||
|
if(ed->dispatcher.joinable())
|
||||||
|
ed->dispatcher.join();
|
||||||
|
|
||||||
|
DeleteCriticalSection(&ed->mutex);
|
||||||
|
delete ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardHook::KeyboardHook() = default;
|
||||||
|
|
||||||
KeyboardHook::KeyboardHook() {}
|
|
||||||
KeyboardHook::~KeyboardHook() {
|
KeyboardHook::~KeyboardHook() {
|
||||||
if(this->_attached)
|
if(this->_attached)
|
||||||
this->detach();
|
this->detach();
|
||||||
@ -19,6 +107,7 @@ bool KeyboardHook::attach() {
|
|||||||
assert(!this->_attached);
|
assert(!this->_attached);
|
||||||
this->active = true;
|
this->active = true;
|
||||||
|
|
||||||
|
init_global_ed();
|
||||||
this->poll_thread = std::thread(std::bind(&KeyboardHook::poll_events, this));
|
this->poll_thread = std::thread(std::bind(&KeyboardHook::poll_events, this));
|
||||||
|
|
||||||
this->_attached = true;
|
this->_attached = true;
|
||||||
@ -34,29 +123,45 @@ void KeyboardHook::detach() {
|
|||||||
if(this->poll_thread.joinable())
|
if(this->poll_thread.joinable())
|
||||||
this->poll_thread.join();
|
this->poll_thread.join();
|
||||||
|
|
||||||
|
/* all events flushed */
|
||||||
|
EnterCriticalSection(&global_event_dispatcher->mutex);
|
||||||
|
WakeAllConditionVariable(&global_event_dispatcher->cv_work);
|
||||||
|
SleepConditionVariableCS(&global_event_dispatcher->cv_flushed, &global_event_dispatcher->mutex, INFINITE);
|
||||||
|
LeaveCriticalSection(&global_event_dispatcher->mutex);
|
||||||
|
shutdown_global_ed();
|
||||||
|
|
||||||
this->_attached = false;
|
this->_attached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT _keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) {
|
LRESULT _keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) {
|
||||||
auto handle = hook_handles[this_thread::get_id()];
|
assert(thread_hook);
|
||||||
assert(handle);
|
auto consume = thread_hook->keyboard_hook_callback(nCode, event, ptr_keyboard);
|
||||||
auto consume = handle->keyboard_hook_callback(nCode, event, ptr_keyboard);
|
|
||||||
if(consume)
|
if(consume)
|
||||||
return 1;
|
return 1;
|
||||||
return CallNextHookEx(nullptr, nCode, event, ptr_keyboard);
|
return CallNextHookEx(nullptr, nCode, event, ptr_keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT _mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) {
|
LRESULT _mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) {
|
||||||
auto handle = hook_handles[this_thread::get_id()];
|
assert(thread_hook);
|
||||||
assert(handle);
|
auto consume = thread_hook->mouse_hook_callback(nCode, event, ptr_keyboard);
|
||||||
auto consume = handle->mouse_hook_callback(nCode, event, ptr_keyboard);
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
if(consume)
|
if(consume)
|
||||||
return 1;
|
return 1;
|
||||||
return CallNextHookEx(nullptr, nCode, event, ptr_keyboard);
|
return CallNextHookEx(nullptr, nCode, event, ptr_keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardHook::poll_events() {
|
void KeyboardHook::poll_events() {
|
||||||
hook_handles[this_thread::get_id()] = this;
|
thread_hook = this;
|
||||||
|
|
||||||
|
if(!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS))
|
||||||
|
std::cerr << "Failed to set priority class to realtime!" << std::endl;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int cur_priority = GetThreadPriority(GetCurrentThread());
|
||||||
|
DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess());
|
||||||
|
std::cout << "P: " << cur_priority << " C: " << cur_priority_class << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
this->keyboad_hook_id = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboard_hook_callback, GetModuleHandle(nullptr), 0);
|
this->keyboad_hook_id = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboard_hook_callback, GetModuleHandle(nullptr), 0);
|
||||||
if(!this->keyboad_hook_id) {
|
if(!this->keyboad_hook_id) {
|
||||||
cerr << "Failed to register keyboard hook" << endl;
|
cerr << "Failed to register keyboard hook" << endl;
|
||||||
@ -75,9 +180,11 @@ void KeyboardHook::poll_events() {
|
|||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
}
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds{1});
|
||||||
|
|
||||||
UnhookWindowsHookEx(this->keyboad_hook_id);
|
UnhookWindowsHookEx(this->mouse_hook_id);
|
||||||
hook_handles[this_thread::get_id()] = nullptr;
|
UnhookWindowsHookEx(this->keyboad_hook_id);
|
||||||
|
thread_hook = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string key_code(DWORD code, bool extended = false) {
|
inline std::string key_code(DWORD code, bool extended = false) {
|
||||||
@ -156,22 +263,59 @@ bool KeyboardHook::keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_ke
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool KeyboardHook::mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_mouse) {
|
bool KeyboardHook::mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_mouse) {
|
||||||
auto mouse = (MouseHookStruct*) ptr_mouse;
|
MouseButtonEventEntry* mb_event;
|
||||||
if(event == WM_RBUTTONDOWN)
|
switch (event) {
|
||||||
this->trigger_key_event(KeyEvent::PRESS, "MOUSE1");
|
case WM_LBUTTONDOWN:
|
||||||
else if(event == WM_RBUTTONUP)
|
mb_event = allocate_mb_event();
|
||||||
this->trigger_key_event(KeyEvent::RELEASE, "MOUSE1");
|
mb_event->type = KeyEvent::PRESS;
|
||||||
if(event == WM_LBUTTONDOWN)
|
mb_event->key = "MOUSE1";
|
||||||
this->trigger_key_event(KeyEvent::PRESS, "MOUSE2");
|
break;
|
||||||
else if(event == WM_LBUTTONUP)
|
case WM_LBUTTONUP:
|
||||||
this->trigger_key_event(KeyEvent::RELEASE, "MOUSE2");
|
mb_event = allocate_mb_event();
|
||||||
if(event == WM_MBUTTONDOWN)
|
mb_event->type = KeyEvent::RELEASE;
|
||||||
this->trigger_key_event(KeyEvent::PRESS, "MOUSE3");
|
mb_event->key = "MOUSE1";
|
||||||
else if(event == WM_MBUTTONUP)
|
break;
|
||||||
this->trigger_key_event(KeyEvent::RELEASE, "MOUSE3");
|
|
||||||
else if(event == WM_XBUTTONDOWN || event == WM_XBUTTONUP) {
|
case WM_RBUTTONDOWN:
|
||||||
auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData);
|
mb_event = allocate_mb_event();
|
||||||
this->trigger_key_event(event == WM_XBUTTONDOWN ? KeyEvent::PRESS : KeyEvent::RELEASE, "MOUSEX" + std::to_string(x_index));
|
mb_event->type = KeyEvent::PRESS;
|
||||||
|
mb_event->key = "MOUSE3";
|
||||||
|
break;
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
mb_event = allocate_mb_event();
|
||||||
|
mb_event->type = KeyEvent::RELEASE;
|
||||||
|
mb_event->key = "MOUSE3";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_XBUTTONDOWN: {
|
||||||
|
auto mouse = (MouseHookStruct*) ptr_mouse;
|
||||||
|
auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData);
|
||||||
|
|
||||||
|
mb_event = allocate_mb_event();
|
||||||
|
mb_event->type = KeyEvent::PRESS;
|
||||||
|
mb_event->key = "MOUSEX" + std::to_string(x_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_XBUTTONUP: {
|
||||||
|
auto mouse = (MouseHookStruct*) ptr_mouse;
|
||||||
|
auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData);
|
||||||
|
|
||||||
|
mb_event = allocate_mb_event();
|
||||||
|
mb_event->type = KeyEvent::RELEASE;
|
||||||
|
mb_event->key = "MOUSEX" + std::to_string(x_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mb_event->next = nullptr;
|
||||||
|
mb_event->hook = thread_hook;
|
||||||
|
|
||||||
|
EnterCriticalSection(&global_event_dispatcher->mutex);
|
||||||
|
*global_event_dispatcher->event_tail = mb_event;
|
||||||
|
global_event_dispatcher->event_tail = &mb_event->next;
|
||||||
|
WakeAllConditionVariable(&global_event_dispatcher->cv_work);
|
||||||
|
LeaveCriticalSection(&global_event_dispatcher->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
@ -10,190 +10,188 @@
|
|||||||
#include "../../audio/AudioOutput.h"
|
#include "../../audio/AudioOutput.h"
|
||||||
#include "../../EventLoop.h"
|
#include "../../EventLoop.h"
|
||||||
|
|
||||||
namespace tc {
|
namespace tc::connection {
|
||||||
namespace connection {
|
class ServerConnection;
|
||||||
class ServerConnection;
|
class VoiceConnection;
|
||||||
class VoiceConnection;
|
class VoiceClient;
|
||||||
class VoiceClient;
|
|
||||||
|
|
||||||
namespace codec {
|
namespace codec {
|
||||||
enum value {
|
enum value {
|
||||||
MIN = 0,
|
MIN = 0,
|
||||||
|
|
||||||
SPEEX_NARROWBAND = 0,
|
SPEEX_NARROWBAND = 0,
|
||||||
SPEEX_WIDEBAND = 1,
|
SPEEX_WIDEBAND = 1,
|
||||||
SPEEX_ULTRA_WIDEBAND = 2,
|
SPEEX_ULTRA_WIDEBAND = 2,
|
||||||
|
|
||||||
CELT_MONO = 3,
|
CELT_MONO = 3,
|
||||||
|
|
||||||
OPUS_VOICE = 4,
|
OPUS_VOICE = 4,
|
||||||
OPUS_MUSIC = 5,
|
OPUS_MUSIC = 5,
|
||||||
|
|
||||||
MAX = 5,
|
MAX = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct condec_info {
|
struct condec_info {
|
||||||
bool supported;
|
bool supported;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::function<std::shared_ptr<audio::codec::Converter>(std::string&)> new_converter;
|
std::function<std::shared_ptr<audio::codec::Converter>(std::string&)> new_converter;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const condec_info info[6];
|
extern const condec_info info[6];
|
||||||
inline const condec_info* get_info(value codec) {
|
inline const condec_info* get_info(value codec) {
|
||||||
if(codec > value::MAX || codec < value::MIN)
|
if(codec > value::MAX || codec < value::MIN)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return &info[codec];
|
return &info[codec];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VoiceClient : private event::EventEntry {
|
class VoiceClient : private event::EventEntry {
|
||||||
friend class VoiceConnection;
|
friend class VoiceConnection;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
template<typename _Tp, typename _Up>
|
template<typename _Tp, typename _Up>
|
||||||
friend _NODISCARD std::shared_ptr<_Tp> std::static_pointer_cast(std::shared_ptr<_Up>&& _Other) noexcept;
|
friend _NODISCARD std::shared_ptr<_Tp> std::static_pointer_cast(std::shared_ptr<_Up>&& _Other) noexcept;
|
||||||
#else
|
#else
|
||||||
template<typename _Tp, typename _Up>
|
template<typename _Tp, typename _Up>
|
||||||
friend inline std::shared_ptr<_Tp> std::static_pointer_cast(const std::shared_ptr<_Up>& __r) noexcept;
|
friend inline std::shared_ptr<_Tp> std::static_pointer_cast(const std::shared_ptr<_Up>& __r) noexcept;
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
struct state {
|
struct state {
|
||||||
enum value {
|
enum value {
|
||||||
buffering, /* this state is never active */
|
buffering, /* this state is never active */
|
||||||
playing,
|
playing,
|
||||||
stopping,
|
stopping,
|
||||||
stopped
|
stopped
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
VoiceClient(const std::shared_ptr<VoiceConnection>& /* connection */, uint16_t /* client id */);
|
VoiceClient(const std::shared_ptr<VoiceConnection>& /* connection */, uint16_t /* client id */);
|
||||||
virtual ~VoiceClient();
|
virtual ~VoiceClient();
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
inline uint16_t client_id() { return this->_client_id; }
|
inline uint16_t client_id() { return this->_client_id; }
|
||||||
|
|
||||||
void initialize_js_object();
|
void initialize_js_object();
|
||||||
void finalize_js_object();
|
void finalize_js_object();
|
||||||
|
|
||||||
v8::Local<v8::Object> js_handle() {
|
v8::Local<v8::Object> js_handle() {
|
||||||
assert(v8::Isolate::GetCurrent());
|
assert(v8::Isolate::GetCurrent());
|
||||||
return this->_js_handle.Get(Nan::GetCurrentContext()->GetIsolate());
|
return this->_js_handle.Get(Nan::GetCurrentContext()->GetIsolate());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::shared_ptr<VoiceClient> ref() { return this->_ref.lock(); }
|
inline std::shared_ptr<VoiceClient> ref() { return this->_ref.lock(); }
|
||||||
|
|
||||||
void process_packet(uint16_t packet_id, const pipes::buffer_view& /* buffer */, codec::value /* codec */, bool /* head */);
|
void process_packet(uint16_t packet_id, const pipes::buffer_view& /* buffer */, codec::value /* codec */, bool /* head */);
|
||||||
|
|
||||||
inline float get_volume() { return this->_volume; }
|
inline float get_volume() { return this->_volume; }
|
||||||
inline void set_volume(float value) { this->_volume = value; }
|
inline void set_volume(float value) { this->_volume = value; }
|
||||||
|
|
||||||
inline state::value state() { return this->_state; }
|
inline state::value state() { return this->_state; }
|
||||||
|
|
||||||
void cancel_replay();
|
void cancel_replay();
|
||||||
|
|
||||||
std::function<void()> on_state_changed;
|
std::function<void()> on_state_changed;
|
||||||
|
|
||||||
inline std::shared_ptr<audio::AudioOutputSource> output_stream() { return this->output_source; }
|
inline std::shared_ptr<audio::AudioOutputSource> output_stream() { return this->output_source; }
|
||||||
private:
|
private:
|
||||||
struct EncodedBuffer {
|
struct EncodedBuffer {
|
||||||
bool head{false};
|
bool head{false};
|
||||||
bool reset_decoder{false};
|
bool reset_decoder{false};
|
||||||
|
|
||||||
std::chrono::system_clock::time_point receive_timestamp;
|
std::chrono::system_clock::time_point receive_timestamp;
|
||||||
|
|
||||||
pipes::buffer buffer;
|
pipes::buffer buffer;
|
||||||
codec::value codec{codec::MIN};
|
codec::value codec{codec::MIN};
|
||||||
|
|
||||||
uint16_t packet_id{0};
|
uint16_t packet_id{0};
|
||||||
EncodedBuffer* next{nullptr};
|
EncodedBuffer* next{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioCodec {
|
struct AudioCodec {
|
||||||
enum struct State {
|
enum struct State {
|
||||||
UNINITIALIZED,
|
UNINITIALIZED,
|
||||||
INITIALIZED_SUCCESSFULLY,
|
INITIALIZED_SUCCESSFULLY,
|
||||||
INITIALIZED_FAIL,
|
INITIALIZED_FAIL,
|
||||||
UNSUPPORTED,
|
UNSUPPORTED,
|
||||||
};
|
};
|
||||||
|
|
||||||
codec::value codec{};
|
codec::value codec{};
|
||||||
|
|
||||||
uint16_t last_packet_id{0xFFFF}; /* the first packet id is 0 so one packet before is 0xFFFF */
|
uint16_t last_packet_id{0xFFFF}; /* the first packet id is 0 so one packet before is 0xFFFF */
|
||||||
std::chrono::system_clock::time_point last_packet_timestamp;
|
std::chrono::system_clock::time_point last_packet_timestamp;
|
||||||
|
|
||||||
inline std::chrono::system_clock::time_point stream_timeout() {
|
inline std::chrono::system_clock::time_point stream_timeout() {
|
||||||
return this->last_packet_timestamp + std::chrono::milliseconds{1000};
|
return this->last_packet_timestamp + std::chrono::milliseconds{1000};
|
||||||
}
|
}
|
||||||
|
|
||||||
State state{State::UNINITIALIZED};
|
State state{State::UNINITIALIZED};
|
||||||
std::shared_ptr<audio::codec::Converter> converter{nullptr};
|
std::shared_ptr<audio::codec::Converter> converter{nullptr};
|
||||||
std::shared_ptr<audio::AudioResampler> resampler{nullptr};
|
std::shared_ptr<audio::AudioResampler> resampler{nullptr};
|
||||||
|
|
||||||
std::mutex pending_lock{};
|
std::mutex pending_lock{};
|
||||||
EncodedBuffer* pending_buffers{nullptr};
|
EncodedBuffer* pending_buffers{nullptr};
|
||||||
|
|
||||||
/* forces all packets which are within the next chain to replay until (inclusive) force_replay is reached */
|
/* forces all packets which are within the next chain to replay until (inclusive) force_replay is reached */
|
||||||
EncodedBuffer* force_replay{nullptr};
|
EncodedBuffer* force_replay{nullptr};
|
||||||
|
|
||||||
bool process_pending{false};
|
bool process_pending{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<AudioCodec, codec::MAX + 1> codec{};
|
std::array<AudioCodec, codec::MAX + 1> codec{};
|
||||||
void initialize_code(const codec::value& /* codec */);
|
void initialize_code(const codec::value& /* codec */);
|
||||||
|
|
||||||
/* might be null (if audio hasn't been initialized) */
|
/* might be null (if audio hasn't been initialized) */
|
||||||
std::shared_ptr<audio::AudioOutputSource> output_source;
|
std::shared_ptr<audio::AudioOutputSource> output_source;
|
||||||
|
|
||||||
std::weak_ptr<VoiceClient> _ref;
|
std::weak_ptr<VoiceClient> _ref;
|
||||||
v8::Persistent<v8::Object> _js_handle;
|
v8::Persistent<v8::Object> _js_handle;
|
||||||
|
|
||||||
uint16_t _client_id{0};
|
uint16_t _client_id{0};
|
||||||
float _volume = 1.f;
|
float _volume = 1.f;
|
||||||
|
|
||||||
std::chrono::system_clock::time_point _last_received_packet;
|
std::chrono::system_clock::time_point _last_received_packet;
|
||||||
state::value _state = state::stopped;
|
state::value _state = state::stopped;
|
||||||
inline void set_state(state::value value) {
|
inline void set_state(state::value value) {
|
||||||
if(value == this->_state)
|
if(value == this->_state)
|
||||||
return;
|
return;
|
||||||
this->_state = value;
|
this->_state = value;
|
||||||
if(this->on_state_changed)
|
if(this->on_state_changed)
|
||||||
this->on_state_changed();
|
this->on_state_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic_bool audio_decode_event_dropped{false};
|
std::atomic_bool audio_decode_event_dropped{false};
|
||||||
void event_execute(const std::chrono::system_clock::time_point &point) override;
|
void event_execute(const std::chrono::system_clock::time_point &point) override;
|
||||||
void event_execute_dropped(const std::chrono::system_clock::time_point &point) override;
|
void event_execute_dropped(const std::chrono::system_clock::time_point &point) override;
|
||||||
|
|
||||||
/* its recommend to call this in correct packet oder */
|
/* its recommend to call this in correct packet oder */
|
||||||
std::shared_ptr<audio::SampleBuffer> decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */);
|
std::shared_ptr<audio::SampleBuffer> decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class VoiceClientWrap : public Nan::ObjectWrap {
|
class VoiceClientWrap : public Nan::ObjectWrap {
|
||||||
public:
|
public:
|
||||||
static NAN_MODULE_INIT(Init);
|
static NAN_MODULE_INIT(Init);
|
||||||
static NAN_METHOD(NewInstance);
|
static NAN_METHOD(NewInstance);
|
||||||
static inline Nan::Persistent<v8::Function> & constructor() {
|
static inline Nan::Persistent<v8::Function> & constructor() {
|
||||||
static Nan::Persistent<v8::Function> my_constructor;
|
static Nan::Persistent<v8::Function> my_constructor;
|
||||||
return my_constructor;
|
return my_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
VoiceClientWrap(const std::shared_ptr<VoiceClient>&);
|
VoiceClientWrap(const std::shared_ptr<VoiceClient>&);
|
||||||
virtual ~VoiceClientWrap();
|
virtual ~VoiceClientWrap();
|
||||||
|
|
||||||
void do_wrap(const v8::Local<v8::Object>&);
|
void do_wrap(const v8::Local<v8::Object>&);
|
||||||
private:
|
private:
|
||||||
static NAN_METHOD(_get_state);
|
static NAN_METHOD(_get_state);
|
||||||
static NAN_METHOD(_get_volume);
|
static NAN_METHOD(_get_volume);
|
||||||
static NAN_METHOD(_set_volume);
|
static NAN_METHOD(_set_volume);
|
||||||
static NAN_METHOD(_abort_replay);
|
static NAN_METHOD(_abort_replay);
|
||||||
static NAN_METHOD(_get_stream);
|
static NAN_METHOD(_get_stream);
|
||||||
|
|
||||||
std::weak_ptr<VoiceClient> _handle;
|
std::weak_ptr<VoiceClient> _handle;
|
||||||
|
|
||||||
bool _currently_playing = false;
|
bool _currently_playing = false;
|
||||||
Nan::callback_t<> call_state_changed;
|
Nan::callback_t<> call_state_changed;
|
||||||
void _call_state_changed();
|
void _call_state_changed();
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "TeaClient",
|
"name": "TeaClient",
|
||||||
"version": "1.4.5-2",
|
"version": "1.4.5-3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user