#pragma once #include #include #include #include #include #include "../../audio/AudioResampler.h" #include "../../audio/codec/Converter.h" #include "../../audio/AudioOutput.h" #include "../../EventLoop.h" namespace tc::connection { class ServerConnection; class VoiceConnection; class VoiceClient; namespace codec { enum value { MIN = 0, SPEEX_NARROWBAND = 0, SPEEX_WIDEBAND = 1, SPEEX_ULTRA_WIDEBAND = 2, CELT_MONO = 3, OPUS_VOICE = 4, OPUS_MUSIC = 5, MAX = 5, }; struct condec_info { bool supported; std::string name; std::function(std::string&)> new_converter; }; extern const condec_info info[6]; inline const condec_info* get_info(value codec) { if(codec > value::MAX || codec < value::MIN) return nullptr; return &info[codec]; } } class VoiceClient : private event::EventEntry { friend class VoiceConnection; #ifdef WIN32 template friend _NODISCARD std::shared_ptr<_Tp> std::static_pointer_cast(std::shared_ptr<_Up>&& _Other) noexcept; #else template friend inline std::shared_ptr<_Tp> std::static_pointer_cast(const std::shared_ptr<_Up>& __r) noexcept; #endif public: struct state { enum value { buffering, /* this state is never active */ playing, stopping, stopped }; }; VoiceClient(const std::shared_ptr& /* connection */, uint16_t /* client id */); virtual ~VoiceClient(); void initialize(); inline uint16_t client_id() const { return this->client_id_; } void initialize_js_object(); void finalize_js_object(); v8::Local js_handle() { assert(v8::Isolate::GetCurrent()); return this->js_handle_.Get(Nan::GetCurrentContext()->GetIsolate()); } inline std::shared_ptr ref() { return this->ref_.lock(); } void process_packet(uint16_t packet_id, const pipes::buffer_view& /* buffer */, codec::value /* codec */, bool /* head */); void execute_tick(); inline float get_volume() const { return this->volume_; } inline void set_volume(float value) { this->volume_ = value; } inline state::value state() { return this->state_; } void cancel_replay(); std::function on_state_changed; inline std::shared_ptr output_stream() { return this->output_source; } private: struct EncodedBuffer { bool head{false}; bool reset_decoder{false}; std::chrono::system_clock::time_point receive_timestamp; pipes::buffer buffer; codec::value codec{codec::MIN}; uint16_t packet_id{0}; EncodedBuffer* next{nullptr}; }; struct AudioCodec { enum struct State { UNINITIALIZED, INITIALIZED_SUCCESSFULLY, INITIALIZED_FAIL, UNSUPPORTED, }; codec::value codec{}; 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; inline std::chrono::system_clock::time_point stream_timeout() const { return this->last_packet_timestamp + std::chrono::milliseconds{1000}; } State state{State::UNINITIALIZED}; std::shared_ptr converter{nullptr}; std::shared_ptr resampler{nullptr}; std::mutex pending_lock{}; EncodedBuffer* pending_buffers{nullptr}; /* forces all packets which are within the next chain to replay until (inclusive) force_replay is reached */ EncodedBuffer* force_replay{nullptr}; bool process_pending{false}; }; std::array codec{}; void initialize_code(const codec::value& /* codec */); /* might be null (if audio hasn't been initialized) */ std::shared_ptr output_source; std::weak_ptr ref_; v8::Persistent js_handle_; uint16_t client_id_{0}; float volume_{1.f}; std::chrono::system_clock::time_point _last_received_packet; state::value state_ = state::stopped; inline void set_state(state::value value) { if(value == this->state_) { return; } this->state_ = value; if(this->on_state_changed) { this->on_state_changed(); } } std::atomic_bool audio_decode_event_dropped{false}; 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; /* its recommend to call this in correct packet oder */ std::shared_ptr decode_buffer(const codec::value& /* codec */,const pipes::buffer_view& /* buffer */, bool /* fec */); }; class VoiceClientWrap : public Nan::ObjectWrap { public: static NAN_MODULE_INIT(Init); static NAN_METHOD(NewInstance); static inline Nan::Persistent & constructor() { static Nan::Persistent my_constructor; return my_constructor; } explicit VoiceClientWrap(const std::shared_ptr&); ~VoiceClientWrap() override; void do_wrap(const v8::Local&); private: static NAN_METHOD(_get_state); static NAN_METHOD(_get_volume); static NAN_METHOD(_set_volume); static NAN_METHOD(_abort_replay); static NAN_METHOD(_get_stream); std::weak_ptr _handle; bool currently_playing_{false}; Nan::callback_t<> call_state_changed; void call_state_changed_(); }; }