diff options
| author | 8cy <[email protected]> | 2020-04-03 02:37:42 -0700 |
|---|---|---|
| committer | 8cy <[email protected]> | 2020-04-03 02:37:42 -0700 |
| commit | 60867fb030bae582082340ead7dbc7efdc2f5398 (patch) | |
| tree | 4c6a7356351be2e4914e15c4703172597c45656e /node_modules/@discordjs/opus/src/node-opus.cc | |
| parent | commenting (diff) | |
| download | s5nical-60867fb030bae582082340ead7dbc7efdc2f5398.tar.xz s5nical-60867fb030bae582082340ead7dbc7efdc2f5398.zip | |
2020/04/03, 02:34, v1.2.0
Diffstat (limited to 'node_modules/@discordjs/opus/src/node-opus.cc')
| -rw-r--r-- | node_modules/@discordjs/opus/src/node-opus.cc | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/node_modules/@discordjs/opus/src/node-opus.cc b/node_modules/@discordjs/opus/src/node-opus.cc new file mode 100644 index 0000000..c63321a --- /dev/null +++ b/node_modules/@discordjs/opus/src/node-opus.cc @@ -0,0 +1,197 @@ +#include "napi.h" +#include "../deps/opus/include/opus.h" +#include "node-opus.h" + +using namespace Napi; + +const char* getDecodeError(int decodedSamples) { + switch(decodedSamples) { + case OPUS_BAD_ARG: + return "One or more invalid/out of range arguments"; + case OPUS_BUFFER_TOO_SMALL: + return "The mode struct passed is invalid"; + case OPUS_INTERNAL_ERROR: + return "An internal error was detected"; + case OPUS_INVALID_PACKET: + return "The compressed data passed is corrupted"; + case OPUS_UNIMPLEMENTED: + return "Invalid/unsupported request number"; + case OPUS_INVALID_STATE: + return "An encoder or decoder structure is invalid or already freed."; + case OPUS_ALLOC_FAIL: + return "Memory allocation has failed"; + default: + return "Unknown OPUS error"; + } +} + +Object OpusEncoder::Init(Napi::Env env, Object exports) { + HandleScope scope(env); + + Function func = DefineClass(env, "OpusEncoder", { + InstanceMethod("encode", &OpusEncoder::Encode), + InstanceMethod("decode", &OpusEncoder::Decode), + InstanceMethod("applyEncoderCTL", &OpusEncoder::ApplyEncoderCTL), + InstanceMethod("applyDecoderCTL", &OpusEncoder::ApplyDecoderCTL), + InstanceMethod("setBitrate", &OpusEncoder::SetBitrate), + InstanceMethod("getBitrate", &OpusEncoder::GetBitrate), + }); + + exports.Set("OpusEncoder", func); + return exports; +} + +OpusEncoder::OpusEncoder(const CallbackInfo& args): ObjectWrap<OpusEncoder>(args) { + this->encoder = nullptr; + this->decoder = nullptr; + this->rate = args[0].ToNumber().Int32Value(); + this->channels = args[1].ToNumber().Int32Value(); + this->application = OPUS_APPLICATION_AUDIO; + this->outPcm = new opus_int16[channels * MAX_FRAME_SIZE]; +} + +OpusEncoder::~OpusEncoder() { + if (this->encoder) opus_encoder_destroy(this->encoder); + if (this->decoder) opus_decoder_destroy(this->decoder); + + this->encoder = nullptr; + this->decoder = nullptr; + + delete this->outPcm; + this->outPcm = nullptr; +} + +int OpusEncoder::EnsureEncoder() { + if (this->encoder) return 0; + + int error; + this->encoder = opus_encoder_create(rate, channels, application, &error); + + return error; +} + +int OpusEncoder::EnsureDecoder() { + if (this->decoder) return 0; + + int error; + this->decoder = opus_decoder_create(rate, channels, &error); + + return error; +} + +Napi::Value OpusEncoder::Encode(const CallbackInfo& args) { + Napi::Env env = args.Env(); + + if (this->EnsureEncoder() != OPUS_OK) { + Napi::Error::New(env, "Could not create encoder. Check the encoder parameters").ThrowAsJavaScriptException(); + } + + if (!args[0].IsBuffer()) { + Napi::TypeError::New(env, "This needs to be a buffer").ThrowAsJavaScriptException(); + } + + Buffer<char> buf = args[0].As<Buffer<char>>(); + char* pcmData = buf.Data(); + opus_int16* pcm = reinterpret_cast<opus_int16*>(pcmData); + int frameSize = buf.Length() / 2 / this->channels; + + int compressedLength = opus_encode(this->encoder, pcm, frameSize, &(this->outOpus[0]), MAX_PACKET_SIZE); + + Buffer<char> actualBuf = Buffer<char>::Copy(env, reinterpret_cast<char*>(this->outOpus), compressedLength); + + if (!actualBuf.IsEmpty()) return actualBuf; +} + +Napi::Value OpusEncoder::Decode(const CallbackInfo& args) { + Napi::Env env = args.Env(); + + Buffer<unsigned char> buf = args[0].As<Buffer<unsigned char>>(); + unsigned char* compressedData = buf.Data(); + size_t compressedDataLength = buf.Length(); + + if (this->EnsureDecoder() != OPUS_OK) { + Napi::Error::New(env, "Could not create decoder. Check the decoder parameters").ThrowAsJavaScriptException(); + } + + int decodedSamples = opus_decode( + this->decoder, + compressedData, + compressedDataLength, + &(this->outPcm[0]), + MAX_FRAME_SIZE, + /* decode_fec */ 0 + ); + + if (decodedSamples < 0) { + Napi::TypeError::New(env, getDecodeError(decodedSamples)).ThrowAsJavaScriptException(); + } + + int decodedLength = decodedSamples * 2 * this->channels; + + Buffer<char> actualBuf = Buffer<char>::Copy(env, reinterpret_cast<char*>(this->outPcm), decodedLength); + + if (!actualBuf.IsEmpty()) return actualBuf; +} + +void OpusEncoder::ApplyEncoderCTL(const CallbackInfo& args) { + Napi::Env env = args.Env(); + + int ctl = args[0].ToNumber().Int32Value(); + int value = args[1].ToNumber().Int32Value(); + + if (this->EnsureEncoder() != OPUS_OK) { + Napi::Error::New(env, "Could not create encoder. Check the encoder parameters").ThrowAsJavaScriptException(); + } + + if (opus_encoder_ctl(this->encoder, ctl, value) != OPUS_OK) { + Napi::TypeError::New(env, "Invalid ctl / value").ThrowAsJavaScriptException(); + } +} + +void OpusEncoder::ApplyDecoderCTL(const CallbackInfo& args) { + Napi::Env env = args.Env(); + + int ctl = args[0].ToNumber().Int32Value(); + int value = args[1].ToNumber().Int32Value(); + + if (this->EnsureDecoder() != OPUS_OK) { + Napi::Error::New(env, "Could not create decoder. Check the decoder parameters").ThrowAsJavaScriptException(); + } + + if (opus_decoder_ctl(this->decoder, ctl, value) != OPUS_OK) { + Napi::TypeError::New(env, "Invalid ctl / value").ThrowAsJavaScriptException(); + } +} + +void OpusEncoder::SetBitrate(const CallbackInfo& args) { + Napi::Env env = args.Env(); + + int bitrate = args[0].ToNumber().Int32Value(); + + if (this->EnsureEncoder() != OPUS_OK) { + Napi::Error::New(env, "Could not create encoder. Check the encoder parameters").ThrowAsJavaScriptException(); + } + + if (opus_encoder_ctl(this->encoder, OPUS_SET_BITRATE(bitrate)) != OPUS_OK) { + Napi::TypeError::New(env, "Invalid bitrate").ThrowAsJavaScriptException(); + } +} + +Napi::Value OpusEncoder::GetBitrate(const CallbackInfo& args) { + Napi::Env env = args.Env(); + + if (this->EnsureEncoder() != OPUS_OK) { + Napi::Error::New(env, "Could not create encoder. Check the encoder parameters").ThrowAsJavaScriptException(); + } + + opus_int32 bitrate; + opus_encoder_ctl(this->encoder, OPUS_GET_BITRATE(&bitrate)); + + return Napi::Number::New(env, bitrate); +} + +Object Init(Napi::Env env, Object exports) { + return OpusEncoder::Init(env, exports); +} + +NODE_API_MODULE(opus, Init) |