TeaSpeak-Client/native/serverconnection/src/audio/js/AudioLevelMeter.cpp

174 lines
6.0 KiB
C++

//
// Created by WolverinDEV on 28/03/2021.
//
#include <include/NanEventCallback.h>
#include <include/NanStrings.h>
#include "./AudioLevelMeter.h"
#include "../AudioLevelMeter.h"
#include "../../logger.h"
using namespace tc::audio;
using namespace tc::audio::recorder;
NAN_MODULE_INIT(AudioLevelMeterWrapper::Init) {
auto klass = Nan::New<v8::FunctionTemplate>(AudioLevelMeterWrapper::NewInstance);
klass->SetClassName(Nan::New("AudioLevelMeter").ToLocalChecked());
klass->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(klass, "start", AudioLevelMeterWrapper::start);
Nan::SetPrototypeMethod(klass, "running", AudioLevelMeterWrapper::running);
Nan::SetPrototypeMethod(klass, "stop", AudioLevelMeterWrapper::stop);
Nan::SetPrototypeMethod(klass, "set_callback", AudioLevelMeterWrapper::set_callback);
Nan::Set(target, Nan::LocalStringUTF8("create_level_meter"), Nan::GetFunction(Nan::New<v8::FunctionTemplate>(AudioLevelMeterWrapper::create_level_meter)).ToLocalChecked());
constructor().Reset(Nan::GetFunction(klass).ToLocalChecked());
}
NAN_METHOD(AudioLevelMeterWrapper::NewInstance) {
if(!info.IsConstructCall()) {
Nan::ThrowError("invalid invoke!");
}
}
NAN_METHOD(AudioLevelMeterWrapper::create_level_meter) {
if(info.Length() != 1 || !info[0]->IsString()) {
Nan::ThrowError("invalid arguments");
return;
}
auto target_device_id = *Nan::Utf8String(info[0]);
std::shared_ptr<AudioDevice> target_device{};
for(const auto& device : audio::devices()) {
if(device->id() == target_device_id) {
target_device = device;
break;
}
}
if(!target_device || !target_device->is_input_supported()) {
Nan::ThrowError("invalid target device");
return;
}
auto wrapper = new AudioLevelMeterWrapper(std::make_shared<AudioLevelMeter>(target_device));
auto js_object = Nan::NewInstance(Nan::New(AudioLevelMeterWrapper::constructor())).ToLocalChecked();
wrapper->wrap(js_object);
info.GetReturnValue().Set(js_object);
}
AudioLevelMeterWrapper::AudioLevelMeterWrapper(std::shared_ptr<AudioLevelMeter> handle) : handle{std::move(handle)} {
log_allocate("AudioLevelMeterWrapper", this);
assert(this->handle);
memset(&this->update_timer, 0, sizeof(this->update_timer));
this->update_timer.data = this;
uv_timer_init(Nan::GetCurrentEventLoop(), &this->update_timer);
}
AudioLevelMeterWrapper::~AudioLevelMeterWrapper() noexcept {
log_free("AudioLevelMeterWrapper", this);
uv_timer_stop(&this->update_timer);
this->update_timer.data = nullptr;
this->callback.Reset();
}
NAN_METHOD(AudioLevelMeterWrapper::start) {
auto handle = ObjectWrap::Unwrap<AudioLevelMeterWrapper>(info.Holder());
if(info.Length() != 1 || !info[0]->IsFunction()) {
Nan::ThrowError("invalid arguments");
return;
}
auto js_queue = std::make_unique<Nan::JavaScriptQueue>();
auto js_callback = std::make_unique<Nan::Persistent<v8::Function>>(info[0].As<v8::Function>());
handle->Ref();
std::thread{[handle, js_queue = std::move(js_queue), js_callback = std::move(js_callback)]() mutable {
std::string error{};
auto result = handle->handle->start(error);
js_queue->enqueue([handle, result, error = std::move(error), js_callback = std::move(js_callback)]{
auto isolate = Nan::GetCurrentContext()->GetIsolate();
auto start_callback = js_callback->Get(isolate);
if(!result) {
auto js_error = Nan::LocalStringUTF8(error);
(void) start_callback->Call(isolate->GetCurrentContext(), Nan::Undefined(), 1, (v8::Local<v8::Value>*) &js_error);
} else {
(void) start_callback->Call(isolate->GetCurrentContext(), Nan::Undefined(), 0, nullptr);
}
handle->test_timer();
js_callback->Reset();
handle->Unref();
});
}}.detach();
}
NAN_METHOD(AudioLevelMeterWrapper::running) {
auto handle = ObjectWrap::Unwrap<AudioLevelMeterWrapper>(info.Holder())->handle;
info.GetReturnValue().Set(handle->running());
}
NAN_METHOD(AudioLevelMeterWrapper::stop) {
auto handle = ObjectWrap::Unwrap<AudioLevelMeterWrapper>(info.Holder());
handle->handle->stop();
handle->test_timer();
}
NAN_METHOD(AudioLevelMeterWrapper::set_callback) {
auto handle = ObjectWrap::Unwrap<AudioLevelMeterWrapper>(info.Holder());
if(info.Length() < 1) {
Nan::ThrowError("invalid arguments");
return;
}
if(info[0]->IsFunction()) {
handle->update_timer_interval = 50;
if(info.Length() >= 2 && info[1]->IsNumber()) {
auto value = info[1]->NumberValue(Nan::GetCurrentContext()).FromMaybe(0);
if(value < 1) {
Nan::ThrowError("invalid update interval");
return;
}
handle->update_timer_interval = (size_t) value;
}
handle->callback.Reset(info[0].As<v8::Function>());
} else if(info[0]->IsNullOrUndefined()) {
handle->callback.Reset();
} else {
Nan::ThrowError("invalid arguments");
return;
}
handle->test_timer();
}
void AudioLevelMeterWrapper::test_timer() {
if(this->handle->running() && !this->callback.IsEmpty()) {
uv_timer_start(&this->update_timer, AudioLevelMeterWrapper::timer_callback, 0, this->update_timer_interval);
} else {
uv_timer_stop(&this->update_timer);
}
}
void AudioLevelMeterWrapper::timer_callback(uv_timer_t *callback) {
Nan::HandleScope scope{};
auto level_meter = (AudioLevelMeterWrapper*) callback->data;
if(level_meter->callback.IsEmpty()) {
return;
}
auto isolate = Nan::GetCurrentContext()->GetIsolate();
assert(isolate);
auto timer_callback = level_meter->callback.Get(isolate);
auto level = Nan::New(level_meter->handle->current_volume());
(void) timer_callback->Call(Nan::GetCurrentContext(), Nan::Undefined(), 1, (v8::Local<v8::Value>*) &level);
}