diff --git a/.gitignore b/.gitignore index 95d2ab6c..7f1aa20e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ snowboydetect.py /swig/Android/java/ /swig/Android/jniLibs/ +/build +/node_modules +/lib/node/binding +/lib/node/index.js diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..58c9c95d --- /dev/null +++ b/.npmignore @@ -0,0 +1,23 @@ +/lib/libsnowboy-detect.a +snowboy-detect-swig.cc +snowboydetect.py +.DS_Store + +*.pyc +*.o +*.so + +/examples/C++/* +/examples/Python/* + +/swig/Android/* +/swig/Python/* + +/build +/node_modules + +/lib/node/*.ts +/lib/node/*.d.ts + +.npmignore +.travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..cef36164 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,87 @@ +language: cpp + +# Cache node dependencies +cache: + directories: + - node_modules + +# Ubuntu 14.04 Trusty support +sudo: required +dist: trusty + +addons: + apt: + sources: + # add PPAs with more up-to-date toolchains + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.9 + packages: + # install toolchains + - libmagic-dev + - libatlas-base-dev + - gcc-5 + - g++-5 + - clang-3.8 + +os: +- linux +- osx + +env: + global: + - secure: Hpft/SbwPrjQbHq+3DeJ8aMCpg2uW4z9MY4XaPPA5FQ80QkUdFMqALRvdBhXf/hm6bEZVLbIMXxqCImL5C4nx1SMUmsL6w/FbJjnamYEopk2MKCPZHKtZOdxsbdUwpL30WRH85DQ0KbcG9LatEr+qLwf9adRQrozhh5zhoRXzjuH8nxS/GRkYuZgTt4wxNt7xYnCVlARS9/V15OeOGcRWw/Q/r++ipINz8ylGqUnTGImZrDZ2nhlOkBSNzrPA7NhCSw1OiGvZpg4zVj/gDkSkPNFn4oDFr1nNDqg0EPFGVXDDI0KA7dpw2DhrJk1z8HgXw8PorPGP0mLnDl4i811KkCz6g6y+ETC6k1VtdB2jss0MCnD9HtxM0RS62yls6Bm5aMhoFjryOHgLHNrjiHfW2/lki421K6QlGp3a2ONkRk9zHiti3uTdtbxlz0kcu7Z8FT045lHNZX0B6QpPiLi2sy7H/dItqAGdWuY0lrGrddX1PpxCckBAZLO8/VEGGGkLQtzbxEXgF+EW0HJxURvUYUF2VCy+kaq86KrFzvSKS/evW/vj7Sq2rNbOCtnIy/rvIKAXU0bbR/1imuEiiMhKdiZku+jRfZZmpjKHoydba9SsHpuNGnR/sH40AIHv7Lv6q+z3mEI+X1YaOVAAlLYWExuHLLbWYjng2gEBIHwmuU= + - secure: RNZDzRXBhS98DMpa0QIKQjL8Nl7Pbo6cYtPyaMjEgF2nv+W+gwhcyDDRUE4psJm26Qkz3AZNfLx/kGKPhhAjBpuGFreCbAFy3uDfbDdcn2K68E+yRSdBAoTIKlxVPpQR11hfPHiAs+3s4BIwLGnuwJSK3JMisboji4ceaxVQpdo0ZcJnNKykN2zabUl+8BW8SYQ8cYp/DLg+wSeqq7eplyYD7zoT/GGnSNylkrRsJxB5zlrRQC/ngUfK7AuxhkfQ14dsdWkkrx0RyVFul5VAc85qAbrtJvLZs2Cu/J3ohNzcRZG7m8+U4diHuIlBFx0ezL3hVBfXkOf74dP8+OnL3rAr/1n+dczl5/5mQqlSsy8UAtUtfdAtd+wRNRy5d+er1YuJBWOGs2SXInjNViEY1Phgs6bY/Lu3wiIxDJH0TORan6ZVSje2/vi7aegRoiqHNrs4m2JuQDCPXu53HKh22+nWgRLLXFT2oBN3FdCz3xj04t+LyT+P5uq9q0jXxKc1nlNpvF3nDzhIuJKcfgBRNm9Wt1vz04xzSRgZEFGMTRWkYTdV+0ZVeqEQjEPo4fRNJ6PT1Tem8VqIoHEKGivGkwiAZ6FhQ/TNkVD7tv5Vhq7eK3ZPXDRakuBsLJ5Nc9QnLCpoEqbuIYqjr8ODKV2HSjS16VaGPbvtYPWzhGKU9C4= + matrix: + - NODE_VERSION="4.0.0" + - NODE_VERSION="5.0.0" + - NODE_VERSION="6.0.0" + +before_install: +# use the correct version of node +- rm -rf ~/.nvm/ && git clone --depth 1 https://github.com/creationix/nvm.git ~/.nvm +- source ~/.nvm/nvm.sh +- nvm install $NODE_VERSION +- nvm use $NODE_VERSION +# get commit message +- COMMIT_MESSAGE=$(git show -s --format=%B $TRAVIS_COMMIT | tr -d '\n') +# put local node-pre-gyp on PATH +- export PATH=./node_modules/.bin/:$PATH +# put global node-gyp on PATH +- npm install node-gyp -g +# install aws-sdk so it is available for publishing +- npm install aws-sdk +# figure out if we should publish or republish +- PUBLISH_BINARY=false +- REPUBLISH_BINARY=false +# if we are building a tag then publish +- if [[ $TRAVIS_BRANCH == `git describe --tags --always HEAD` ]]; then PUBLISH_BINARY=true; fi; +# or if we put [publish binary] in the commit message +- if test "${COMMIT_MESSAGE#*'[publish binary]'}" != "$COMMIT_MESSAGE"; then PUBLISH_BINARY=true; fi; +# alternativly we can [republish binary] which will replace any existing binary +- if test "${COMMIT_MESSAGE#*'[republish binary]'}" != "$COMMIT_MESSAGE"; then PUBLISH_BINARY=true && REPUBLISH_BINARY=true; fi; +install: +# ensure source install works +- npm install --build-from-source +# test our module +- node lib/node/index.js + +before_script: +# if publishing, do it +- if [[ $REPUBLISH_BINARY == true ]]; then node-pre-gyp package unpublish; fi; +- if [[ $PUBLISH_BINARY == true ]]; then node-pre-gyp package publish; fi; +# cleanup +- node-pre-gyp clean +- node-gyp clean + +script: +# if publishing, test installing from remote +- INSTALL_RESULT=0 +- if [[ $PUBLISH_BINARY == true ]]; then INSTALL_RESULT=$(npm install --fallback-to-build=false > /dev/null)$? || true; fi; +# if install returned non zero (errored) then we first unpublish and then call false so travis will bail at this line +- if [[ $INSTALL_RESULT != 0 ]]; then echo "returned $INSTALL_RESULT";node-pre-gyp unpublish;false; fi +# If success then we arrive here so lets clean up +- node-pre-gyp clean + +after_success: +# if success then query and display all published binaries +- node-pre-gyp info diff --git a/README.md b/README.md index 593878b5..3d46a8fb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ by [KITT.AI](http://kitt.ai). (The discussion group is new since September 2016 as we are getting many messages every day. Please send general questions there. For bugs, use Github issues.) -Version: 1.0.4 (7/13/2016) +Version: 1.1.0 (9/20/2016) Snowboy is a customizable hotword detection engine for you to create your own hotword like "OK Google" or "Alexa". It is powered by deep neural networks and @@ -42,12 +42,24 @@ pull request! If you want support on other hardware/OS, please send your request to [snowboy@kitt.ai](mailto:snowboy.kitt.ai) +## Precompiled node module + +Snowboy is available in the form of a native node module precompiled for: +64 bit Ubuntu, MacOS X, and the Raspberry Pi (Raspbian 8.0+). For quick +installation run: + + npm install --save snowboy + +For sample usage see the `examples/Node` folder. You may have to install +dependencies like `fs`, `wav` or `node-record-lpcm16` depending on which script +you use. + ## Precompiled Binaries with Python Demo -* 64 bit Ubuntu [12.04](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/ubuntu1204-x86_64-1.0.4.tar.bz2) - / [14.04](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/ubuntu1404-x86_64-1.0.4.tar.bz2) -* [MacOS X](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/osx-x86_64-1.0.4.tar.bz2) +* 64 bit Ubuntu [12.04](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/ubuntu1204-x86_64-1.1.0.tar.bz2) + / [14.04](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/ubuntu1404-x86_64-1.1.0.tar.bz2) +* [MacOS X](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/osx-x86_64-1.1.0.tar.bz2) * Raspberry Pi with Raspbian 8.0, all versions - ([1/2/3/Zero](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/rpi-arm-raspbian-8.0-1.0.4.tar.bz2)) + ([1/2/3/Zero](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/rpi-arm-raspbian-8.0-1.1.0.tar.bz2)) If you want to compile a version against your own environment/language, read on. @@ -85,6 +97,15 @@ Make sure that you can record audio with your microphone: If you need extra setup on your audio (especially on a Raspberry Pi), please see the [full documentation](http://docs.kitt.ai/snowboy). +## Compile a Node addon +Compiling a node addon for Linux and the Raspberry Pi requires the installation of the following dependencies: + + sudo apt-get install libmagic-dev libatlas-base-dev + +Then to compile the addon run the following from the root of the snowboy repository: + + node-pre-gyp clean configure build + ## Compile a Python Wrapper cd swig/Python @@ -172,6 +193,13 @@ See [Full Documentation](http://docs.kitt.ai/snowboy). ## Change Log +**v1.1.0, 9/20/2016** + +* Added library for Node. +* Added support for Python3. +* Added universal model `alexa.umdl` +* Updated universal model `snowboy.umdl` so that it works in noisy environment. + **v1.0.4, 7/13/2016** * Updated universal `snowboy.umdl` model to make it more robust. diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 00000000..77a2af60 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,69 @@ +{ + 'targets': [{ + 'target_name': 'snowboy', + 'sources': [ + 'swig/Node/snowboy.cc' + ], + 'conditions': [ + ['OS=="mac"', { + 'link_settings': { + 'libraries': [ + '<(module_root_dir)/lib/osx/libsnowboy-detect.a', + ] + } + }], + ['OS=="linux" and target_arch=="x64"', { + 'link_settings': { + 'libraries': [ + '<(module_root_dir)/lib/ubuntu64/libsnowboy-detect.a', + ] + } + }], + ['OS=="linux" and target_arch=="arm"', { + 'link_settings': { + 'libraries': [ + '<(module_root_dir)/lib/rpi/libsnowboy-detect.a', + ] + } + }] + ], + 'cflags': [ + '-std=c++11', + '-fexceptions', + '-Wall', + '-D_GLIBCXX_USE_CXX11_ABI=0' + ], + 'cflags!': [ + '-fno-exceptions' + ], + 'cflags_cc!': [ + '-fno-exceptions' + ], + 'include_dirs': [ + "; +} + +interface HotwordModelsInterface { + add(model: HotwordModel): void; + lookup(index: number): string; + numHotwords(): number; +} + +interface DetectorOptions { + resource: string; + models: HotwordModels; + audioGain?: number; +} + +interface SnowboyDetectInterface { + reset(): boolean; + runDetection(buffer: Buffer): number; + setSensitivity(sensitivity: string): void; + getSensitivity(): string; + setAudioGain(gain: number): void; + updateModel(): void; + numHotwords(): number; + sampleRate(): number; + numChannels(): number; + bitsPerSample(): number; +} + +export class HotwordModels implements HotwordModels { + private models: Array = []; + private lookupTable: Array; + + add(model: HotwordModel) { + model.hotwords = [].concat(model.hotwords); + model.sensitivity = model.sensitivity || "0.5"; + + if (fs.existsSync(model.file) === false) { + throw new Error(`Model ${model.file} does not exists.`); + } + + const type = path.extname(model.file).toUpperCase(); + + if (ModelType[type] === ModelType.PMDL && model.hotwords.length > 1) { + throw new Error('Personal models can define only one hotword.'); + } + + this.models.push(model); + this.lookupTable = this.generateHotwordsLookupTable(); + } + + get modelString(): string { + return this.models.map((model) => model.file).join(); + } + + get sensitivityString(): string { + return this.models.map((model) => model.sensitivity).join(); + } + + lookup(index: number): string { + const lookupIndex = index - 1; + if (lookupIndex < 0 || lookupIndex >= this.lookupTable.length) { + throw new Error('Index out of bounds.'); + } + return this.lookupTable[lookupIndex]; + } + + numHotwords(): number { + return this.lookupTable.length; + } + + private generateHotwordsLookupTable(): Array { + return this.models.reduce((hotwords, model) => { + return hotwords.concat(model.hotwords); + }, new Array()); + } +} + +export class SnowboyDetect extends stream.Writable implements SnowboyDetectInterface { + nativeInstance: SnowboyDetectNativeInterface; + private models: HotwordModels; + + constructor(options: DetectorOptions) { + super(); + + this.models = options.models; + this.nativeInstance = new SnowboyDetectNative(options.resource, options.models.modelString); + + if (this.nativeInstance.NumHotwords() !== options.models.numHotwords()) { + throw new Error('Loaded hotwords count does not match number of hotwords defined.'); + } + + this.nativeInstance.SetSensitivity(options.models.sensitivityString); + + if (options.audioGain) { + this.nativeInstance.SetAudioGain(options.audioGain); + } + } + + reset(): boolean { + return this.nativeInstance.Reset(); + } + + runDetection(buffer: Buffer): number { + const index = this.nativeInstance.RunDetection(buffer); + this.processDetectionResult(index); + return index; + } + + setSensitivity(sensitivity: string): void { + this.nativeInstance.SetSensitivity(sensitivity); + } + + getSensitivity(): string { + return this.nativeInstance.GetSensitivity(); + } + + setAudioGain(gain: number): void { + this.nativeInstance.SetAudioGain(gain); + } + + updateModel(): void { + this.nativeInstance.UpdateModel(); + } + + numHotwords(): number { + return this.nativeInstance.NumHotwords(); + } + + sampleRate(): number { + return this.nativeInstance.SampleRate(); + } + + numChannels(): number { + return this.nativeInstance.NumChannels(); + } + + bitsPerSample(): number { + return this.nativeInstance.BitsPerSample(); + } + + // Stream implementation + _write(chunk: Buffer, encoding: string, callback: Function) { + const index = this.nativeInstance.RunDetection(chunk); + this.processDetectionResult(index); + return callback(); + } + + private processDetectionResult(index: number): void { + switch (index) { + case DetectionResult.ERROR: + this.emit('error'); + break; + + case DetectionResult.SILENCE: + this.emit('silence'); + break; + + case DetectionResult.SOUND: + this.emit('sound'); + break; + + default: + const hotword = this.models.lookup(index); + this.emit('hotword', index, hotword); + break; + } + } +} + +export const Detector = SnowboyDetect; +export const Models = HotwordModels; diff --git a/lib/node/node-pre-gyp.d.ts b/lib/node/node-pre-gyp.d.ts new file mode 100644 index 00000000..22b07a70 --- /dev/null +++ b/lib/node/node-pre-gyp.d.ts @@ -0,0 +1,3 @@ +declare module "node-pre-gyp" { + export function find(path:string):string; +} diff --git a/lib/osx/libsnowboy-detect.a b/lib/osx/libsnowboy-detect.a index 78fea82b..3401dae8 100644 Binary files a/lib/osx/libsnowboy-detect.a and b/lib/osx/libsnowboy-detect.a differ diff --git a/lib/rpi/libsnowboy-detect.a b/lib/rpi/libsnowboy-detect.a index 711d30d9..702073e2 100644 Binary files a/lib/rpi/libsnowboy-detect.a and b/lib/rpi/libsnowboy-detect.a differ diff --git a/lib/ubuntu64/libsnowboy-detect.a b/lib/ubuntu64/libsnowboy-detect.a index 8a784ee0..9cdc00f7 100644 Binary files a/lib/ubuntu64/libsnowboy-detect.a and b/lib/ubuntu64/libsnowboy-detect.a differ diff --git a/package.json b/package.json new file mode 100644 index 00000000..2c7ffb68 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "snowboy", + "version": "1.1.0", + "description": "Snowboy is a customizable hotword detection engine", + "main": "lib/node/index.js", + "binary": { + "module_name": "snowboy", + "module_path": "./lib/node/binding/{configuration}/{node_abi}-{platform}-{arch}/", + "remote_path": "./{module_name}/v{version}/{configuration}/", + "package_name": "{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz", + "host": "https://snowboy-release-node.s3-us-west-2.amazonaws.com" + }, + "scripts": { + "preinstall": "npm install node-pre-gyp", + "install": "node-pre-gyp install --fallback-to-build", + "test": "node index.js", + "prepublish": "tsc --listFiles" + }, + "author": "KITT.AI ", + "contributors": [ + "Leandre Gohy ", + "Evan Cohen " + ], + "repository": { + "type": "git", + "url": "git+https://github.com/Kitt-AI/snowboy.git" + }, + "gypfile": true, + "license": "Apache-2.0", + "dependencies": { + "node-pre-gyp": "^0.6.30" + }, + "devDependencies": { + "@types/node": "^6.0.38", + "aws-sdk": "2.x", + "nan": "^2.4.0", + "typescript": "^2.0.2" + }, + "bugs": { + "url": "https://github.com/Kitt-AI/snowboy/issues" + } +} diff --git a/resources/alexa.umdl b/resources/alexa.umdl new file mode 100644 index 00000000..0d9db6f2 Binary files /dev/null and b/resources/alexa.umdl differ diff --git a/resources/snowboy.umdl b/resources/snowboy.umdl index b952ca60..bb68185c 100644 Binary files a/resources/snowboy.umdl and b/resources/snowboy.umdl differ diff --git a/resources/snowboy.wav b/resources/snowboy.wav new file mode 100644 index 00000000..8ce41003 Binary files /dev/null and b/resources/snowboy.wav differ diff --git a/scripts/publish-node.sh b/scripts/publish-node.sh new file mode 100755 index 00000000..ab95bcf8 --- /dev/null +++ b/scripts/publish-node.sh @@ -0,0 +1,23 @@ +#!/bin/bash + + +NODE_VERSIONS=( "4.0.0" "5.0.0" "6.0.0" ) + +# Makes sure nvm is installed. +if ! which nvm > /dev/null; then + rm -rf ~/.nvm/ &&\ + git clone --depth 1 https://github.com/creationix/nvm.git ~/.nvm + source ~/.nvm/nvm.sh +fi + +for i in "${NODE_VERSIONS[@]}"; do + # Installs and use the correct version of node + nvm install $i + nvm use $i + + # build, package and publish for the current package version + npm install nan + npm install aws-sdk + npm install node-pre-gyp + ./node_modules/.bin/node-pre-gyp clean configure build package publish +done diff --git a/swig/Android/Makefile b/swig/Android/Makefile index 59424207..2e263456 100644 --- a/swig/Android/Makefile +++ b/swig/Android/Makefile @@ -4,8 +4,7 @@ # When you extract Android toolchain from Android NDK, make sure you supply # --stl=libc++ option. This Makefile is optimized for armv7-a architecture. -# Some versions of swig does not work well. We prefer compiling swig from source -# code. We have tested swig-3.0.7.tar.gz. +# Please use swig-3.0.10 or up. SWIG := swig # Please specify your NDK root directory here. diff --git a/swig/Node/snowboy.cc b/swig/Node/snowboy.cc new file mode 100644 index 00000000..ea020fba --- /dev/null +++ b/swig/Node/snowboy.cc @@ -0,0 +1,173 @@ +#include +#include +#include + +class SnowboyDetect : public Nan::ObjectWrap { + public: + static NAN_MODULE_INIT(Init); + + private: + explicit SnowboyDetect(const std::string& resource_filename, + const std::string& model_str); + ~SnowboyDetect(); + + static NAN_METHOD(New); + static NAN_METHOD(Reset); + static NAN_METHOD(RunDetection); + static NAN_METHOD(SetSensitivity); + static NAN_METHOD(GetSensitivity); + static NAN_METHOD(SetAudioGain); + static NAN_METHOD(UpdateModel); + static NAN_METHOD(NumHotwords); + static NAN_METHOD(SampleRate); + static NAN_METHOD(NumChannels); + static NAN_METHOD(BitsPerSample); + + static Nan::Persistent constructor; + + snowboy::SnowboyDetect* detector; +}; + +Nan::Persistent SnowboyDetect::constructor; + +SnowboyDetect::SnowboyDetect(const std::string& resource_filename, + const std::string& model_str) { + try { + this->detector = new snowboy::SnowboyDetect(resource_filename, model_str); + } catch (std::runtime_error e) { + Nan::ThrowError(e.what()); + } +} +SnowboyDetect::~SnowboyDetect() { + if (this->detector) { + delete this->detector; + } +} + +NAN_MODULE_INIT(SnowboyDetect::Init) { + v8::Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("SnowboyDetect").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + SetPrototypeMethod(tpl, "Reset", Reset); + SetPrototypeMethod(tpl, "RunDetection", RunDetection); + SetPrototypeMethod(tpl, "SetSensitivity", SetSensitivity); + SetPrototypeMethod(tpl, "GetSensitivity", GetSensitivity); + SetPrototypeMethod(tpl, "SetAudioGain", SetAudioGain); + SetPrototypeMethod(tpl, "UpdateModel", UpdateModel); + SetPrototypeMethod(tpl, "NumHotwords", NumHotwords); + SetPrototypeMethod(tpl, "SampleRate", SampleRate); + SetPrototypeMethod(tpl, "NumChannels", NumChannels); + SetPrototypeMethod(tpl, "BitsPerSample", BitsPerSample); + + constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked()); + Nan::Set(target, Nan::New("SnowboyDetect").ToLocalChecked(), + Nan::GetFunction(tpl).ToLocalChecked()); +} + +NAN_METHOD(SnowboyDetect::New) { + if (!info.IsConstructCall()) { + Nan::ThrowError("Cannot call constructor as function, you need to use " + "'new' keyword"); + return; + } else if (!info[0]->IsString()) { + Nan::ThrowTypeError("resource must be a string"); + return; + } else if (!info[1]->IsString()) { + Nan::ThrowTypeError("model must be a string"); + return; + } + + Nan::MaybeLocal resource = Nan::To(info[0]); + Nan::MaybeLocal model = Nan::To(info[1]); + Nan::Utf8String resourceString(resource.ToLocalChecked()); + Nan::Utf8String modelString(model.ToLocalChecked()); + SnowboyDetect* obj = new SnowboyDetect(*resourceString, *modelString); + obj->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); +} + +NAN_METHOD(SnowboyDetect::Reset) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + bool ret = ptr->detector->Reset(); + info.GetReturnValue().Set(Nan::New(ret)); +} + +NAN_METHOD(SnowboyDetect::RunDetection) { + if (!info[0]->IsObject()) { + Nan::ThrowTypeError("data must be a buffer"); + return; + } + + Nan::MaybeLocal buffer = Nan::To(info[0]); + char* bufferData = node::Buffer::Data(buffer.ToLocalChecked()); + size_t bufferLength = node::Buffer::Length(buffer.ToLocalChecked()); + + std::string data(bufferData, bufferLength); + + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + int ret = ptr->detector->RunDetection(data); + info.GetReturnValue().Set(Nan::New(ret)); +} + +NAN_METHOD(SnowboyDetect::SetSensitivity) { + if (!info[0]->IsString()) { + Nan::ThrowTypeError("sensitivity must be a string"); + return; + } + + Nan::MaybeLocal sensitivity = Nan::To(info[0]); + Nan::Utf8String sensitivityString(sensitivity.ToLocalChecked()); + + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + ptr->detector->SetSensitivity(*sensitivityString); +} + +NAN_METHOD(SnowboyDetect::GetSensitivity) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + std::string sensitivity = ptr->detector->GetSensitivity(); + info.GetReturnValue().Set(Nan::New(sensitivity).ToLocalChecked()); +} + +NAN_METHOD(SnowboyDetect::SetAudioGain) { + if (!info[0]->IsNumber()) { + Nan::ThrowTypeError("gain must be a number"); + return; + } + + Nan::MaybeLocal gain = Nan::To(info[0]); + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + ptr->detector->SetAudioGain(gain.ToLocalChecked()->Value()); +} + +NAN_METHOD(SnowboyDetect::UpdateModel) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + ptr->detector->UpdateModel(); +} + +NAN_METHOD(SnowboyDetect::NumHotwords) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + int numHotwords = ptr->detector->NumHotwords(); + info.GetReturnValue().Set(Nan::New(numHotwords)); +} + +NAN_METHOD(SnowboyDetect::SampleRate) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + int sampleRate = ptr->detector->SampleRate(); + info.GetReturnValue().Set(Nan::New(sampleRate)); +} + +NAN_METHOD(SnowboyDetect::NumChannels) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + int numChannels = ptr->detector->NumChannels(); + info.GetReturnValue().Set(Nan::New(numChannels)); +} + +NAN_METHOD(SnowboyDetect::BitsPerSample) { + SnowboyDetect* ptr = Nan::ObjectWrap::Unwrap(info.Holder()); + int bitsPerSample = ptr->detector->BitsPerSample(); + info.GetReturnValue().Set(Nan::New(bitsPerSample)); +} + + +NODE_MODULE(SnowboyDetect, SnowboyDetect::Init) diff --git a/swig/Python/Makefile b/swig/Python/Makefile index 5f2657b2..c03a9a35 100644 --- a/swig/Python/Makefile +++ b/swig/Python/Makefile @@ -1,8 +1,7 @@ # Example Makefile that converts snowboy c++ library (snowboy-detect.a) to # python library (_snowboydetect.so, snowboydetect.py), using swig. -# Some versions of swig does not work well. We prefer compiling swig from source -# code. We have tested swig-3.0.7.tar.gz. +# Please use swig-3.0.10 or up. SWIG := swig SNOWBOYDETECTSWIGITF = snowboy-detect-swig.i @@ -16,8 +15,8 @@ LDFLAGS := ifeq ($(shell uname), Darwin) CXX := clang++ - PYINC := $(shell /usr/bin/python2.7-config --includes) - PYLIBS := $(shell /usr/bin/python2.7-config --ldflags) + PYINC := $(shell python-config --includes) + PYLIBS := $(shell python-config --ldflags) SWIGFLAGS := -bundle -flat_namespace -undefined suppress LDLIBS := -lm -ldl -framework Accelerate SNOWBOYDETECTLIBFILE = $(TOPDIR)/lib/osx/libsnowboy-detect.a diff --git a/swig/Python/snowboy-detect-swig.i b/swig/Python/snowboy-detect-swig.i index 76633993..c06b830e 100644 --- a/swig/Python/snowboy-detect-swig.i +++ b/swig/Python/snowboy-detect-swig.i @@ -13,3 +13,7 @@ %} %include "include/snowboy-detect.h" + +%begin %{ +#define SWIG_PYTHON_STRICT_BYTE_CHAR +%} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..e74e67e8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "isolatedModules": false, + "jsx": "react", + "experimentalDecorators": false, + "emitDecoratorMetadata": false, + "declaration": false, + "noImplicitAny": true, + "noImplicitUseStrict": false, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "removeComments": true, + "noLib": false, + "preserveConstEnums": true, + "suppressImplicitAnyIndexErrors": true + }, + "files": [ + "lib/node/index.ts", + "lib/node/node-pre-gyp.d.ts", + "lib/node/SnowboyDetectNative.d.ts", + "node_modules/@types/node/index.d.ts" + ], + "exclude": [ + "node_modules" + ], + "compileOnSave": true, + "buildOnSave": false, + "atom": { + "rewriteTsconfig": false + } +}