#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 { namespace 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; template friend inline std::shared_ptr<_Tp> std::static_pointer_cast(const std::shared_ptr<_Up>& __r) noexcept; 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(); inline uint16_t client_id() { 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 */); inline float get_volume() { 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 AudioCodec { uint16_t last_packet_id = 0; std::chrono::system_clock::time_point last_packet_timestamp; bool successfully_initialized; std::shared_ptr converter; std::shared_ptr resampler; }; std::array, codec::MAX + 1> codec{ nullptr, nullptr, nullptr, nullptr, nullptr }; std::shared_ptr output_source; std::weak_ptr _ref; v8::Persistent _js_handle; uint16_t _client_id; 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(); } struct EncodedBuffer { bool head; uint16_t packet_id; pipes::buffer buffer; codec::value codec; std::chrono::system_clock::time_point receive_timestamp; }; std::mutex audio_decode_queue_lock; std::deque> audio_decode_queue; void event_execute(const std::chrono::system_clock::time_point &point) override; void process_encoded_buffer(const std::unique_ptr& /* buffer */); }; 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; } VoiceClientWrap(const std::shared_ptr&); virtual ~VoiceClientWrap(); 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(); }; } }