diff --git a/native_client/README.md b/native_client/README.md index 52f65bbf..c3d15919 100644 --- a/native_client/README.md +++ b/native_client/README.md @@ -100,7 +100,7 @@ After following the above build and installation instructions, the Node.JS bindi ``` cd native_client/javascript -make package +make build make npm-pack ``` diff --git a/native_client/javascript/client.js b/native_client/javascript/client.js index 94473ce3..c831f058 100644 --- a/native_client/javascript/client.js +++ b/native_client/javascript/client.js @@ -1,5 +1,4 @@ #!/usr/bin/env node - 'use strict'; const Fs = require('fs'); @@ -69,6 +68,7 @@ function metadataToString(metadata) { for (var i = 0; i < metadata.num_items; ++i) { retval += metadata.items[i].character; } + Ds.FreeMetadata(metadata); return retval; } @@ -135,5 +135,6 @@ audioStream.on('finish', () => { } const inference_stop = process.hrtime(inference_start); console.error('Inference took %ds for %ds audio file.', totalTime(inference_stop), audioLength.toPrecision(4)); + Ds.DestroyModel(model); process.exit(0); }); diff --git a/native_client/javascript/deepspeech.i b/native_client/javascript/deepspeech.i index 130573f2..b22b863e 100644 --- a/native_client/javascript/deepspeech.i +++ b/native_client/javascript/deepspeech.i @@ -28,7 +28,6 @@ using namespace node; // make sure the string returned by SpeechToText is freed %typemap(newfree) char* "DS_FreeString($1);"; -%typemap(newfree) Metadata* "DS_FreeMetadata($1);"; %newobject DS_SpeechToText; %newobject DS_IntermediateDecode; @@ -61,19 +60,8 @@ using namespace node; %append_output(SWIG_NewPointerObj(%as_voidptr(*$1), $*1_descriptor, 0)); } -// extend ModelState with a destructor so that DestroyModel will be called -// when the JavaScript object gets finalized. %nodefaultctor ModelState; %nodefaultdtor ModelState; - -struct ModelState {}; - -%extend ModelState { - ~ModelState() { - DS_DestroyModel($self); - } -} - %nodefaultdtor Metadata; %nodefaultctor Metadata; %nodefaultctor MetadataItem; @@ -94,9 +82,6 @@ struct ModelState {}; v8::Handle result = SWIGV8_ARRAY_NEW(); return result; } - ~Metadata() { - DS_FreeMetadata($self); - } } %rename ("%(strip:[DS_])s") ""; diff --git a/native_client/javascript/index.js b/native_client/javascript/index.js index bf996385..4eb693f0 100644 --- a/native_client/javascript/index.js +++ b/native_client/javascript/index.js @@ -75,7 +75,13 @@ Model.prototype.finishStreamWithMetadata = function() { return binding.FinishStreamWithMetadata.apply(null, arguments); } +function DestroyModel(model) { + return binding.DestroyModel(model._impl); +} + module.exports = { Model: Model, - printVersions: binding.PrintVersions + printVersions: binding.PrintVersions, + DestroyModel: DestroyModel, + FreeMetadata: binding.FreeMetadata }; diff --git a/native_client/python/client.py b/native_client/python/client.py index 2a6a6242..d52fa6b1 100644 --- a/native_client/python/client.py +++ b/native_client/python/client.py @@ -39,6 +39,7 @@ N_FEATURES = 26 # Size of the context window used for producing timesteps in the input vector N_CONTEXT = 9 + def convert_samplerate(audio_path): sox_cmd = 'sox {} --type raw --bits 16 --channels 1 --rate 16000 --encoding signed-integer --endian little --compression 0.0 --no-dither - '.format(quote(audio_path)) try: @@ -50,11 +51,9 @@ def convert_samplerate(audio_path): return 16000, np.frombuffer(output, np.int16) + def metadata_to_string(metadata): - retval = '' - for item in range(metadata.num_items): - retval += metadata.items[item].character - return retval + return ''.join(item.character for item in metadata.items) class VersionAction(argparse.Action): @@ -65,6 +64,7 @@ class VersionAction(argparse.Action): printVersions() exit(0) + def main(): parser = argparse.ArgumentParser(description='Running DeepSpeech inference.') parser.add_argument('--model', required=True, diff --git a/native_client/python/impl.i b/native_client/python/impl.i index c5ad1161..6d542877 100644 --- a/native_client/python/impl.i +++ b/native_client/python/impl.i @@ -33,17 +33,19 @@ import_array(); %append_output(SWIG_NewPointerObj(%as_voidptr(*$1), $*1_descriptor, 0)); } -%extend struct MetadataItem { - MetadataItem* __getitem__(size_t i) { - return &$self[i]; - } -} - %typemap(out) Metadata* { // owned, extended destructor needs to be called by SWIG %append_output(SWIG_NewPointerObj(%as_voidptr($1), $1_descriptor, SWIG_POINTER_OWN)); } +%typemap(out) MetadataItem* %{ + $result = PyList_New(arg1->num_items); + for (int i = 0; i < arg1->num_items; ++i) { + PyObject* o = SWIG_NewPointerObj(SWIG_as_voidptr(&arg1->items[i]), SWIGTYPE_p_MetadataItem, 0); + PyList_SetItem($result, i, o); + } +%} + %extend struct Metadata { ~Metadata() { DS_FreeMetadata($self); diff --git a/native_client/python/setup.cfg b/native_client/python/setup.cfg index 8aa51594..a7fa93e6 100644 --- a/native_client/python/setup.cfg +++ b/native_client/python/setup.cfg @@ -1,6 +1,5 @@ [build_ext] include-dirs=./ -swig-opts=-c++ -keyword build-lib=temp_build build-temp=temp_build diff --git a/native_client/python/setup.py b/native_client/python/setup.py index b9e68c67..c8cb6b62 100755 --- a/native_client/python/setup.py +++ b/native_client/python/setup.py @@ -7,93 +7,101 @@ import os import subprocess import sys -try: - import numpy +def main(): try: - numpy_include = numpy.get_include() - except AttributeError: - numpy_include = numpy.get_numpy_include() -except ImportError: - numpy_include = '' - assert 'NUMPY_INCLUDE' in os.environ + import numpy + try: + numpy_include = numpy.get_include() + except AttributeError: + numpy_include = numpy.get_numpy_include() + except ImportError: + numpy_include = '' + assert 'NUMPY_INCLUDE' in os.environ -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() + def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() -numpy_include = os.getenv('NUMPY_INCLUDE', numpy_include) -numpy_min_ver = os.getenv('NUMPY_DEP_VERSION', '') + numpy_include = os.getenv('NUMPY_INCLUDE', numpy_include) + numpy_min_ver = os.getenv('NUMPY_DEP_VERSION', '') -project_name = 'deepspeech' -if '--project_name' in sys.argv: - project_name_idx = sys.argv.index('--project_name') - project_name = sys.argv[project_name_idx + 1] - sys.argv.remove('--project_name') - sys.argv.pop(project_name_idx) + project_name = 'deepspeech' + if '--project_name' in sys.argv: + project_name_idx = sys.argv.index('--project_name') + project_name = sys.argv[project_name_idx + 1] + sys.argv.remove('--project_name') + sys.argv.pop(project_name_idx) -with open('../../VERSION', 'r') as ver: - project_version = ver.read().strip() + with open('../../VERSION', 'r') as ver: + project_version = ver.read().strip() -class BuildExtFirst(build): - sub_commands = [('build_ext', build.has_ext_modules), - ('build_py', build.has_pure_modules), - ('build_clib', build.has_c_libraries), - ('build_scripts', build.has_scripts)] + class BuildExtFirst(build): + sub_commands = [('build_ext', build.has_ext_modules), + ('build_py', build.has_pure_modules), + ('build_clib', build.has_c_libraries), + ('build_scripts', build.has_scripts)] -# Properly pass arguments for linking, setuptools will perform some checks -def lib_dirs_split(a): - if os.name == 'posix': - return a.split('-L')[1:] + # Properly pass arguments for linking, setuptools will perform some checks + def lib_dirs_split(a): + if os.name == 'posix': + return a.split('-L')[1:] - if os.name == 'nt': - return [] + if os.name == 'nt': + return [] -def libs_split(a): - if os.name == 'posix': - return a.split('-l')[1:] + raise AssertionError('os.name == java not expected') - if os.name == 'nt': - return a.split('.lib')[0:1] + def libs_split(a): + if os.name == 'posix': + return a.split('-l')[1:] -ds_ext = Extension('deepspeech._impl', - ['impl.i'], - include_dirs = [ numpy_include, '../' ], - library_dirs = list(map(lambda x: x.strip(), lib_dirs_split(os.getenv('MODEL_LDFLAGS', '')))), - libraries = list(map(lambda x: x.strip(), libs_split(os.getenv('MODEL_LIBS', '')))) - ) + if os.name == 'nt': + return a.split('.lib')[0:1] -setup(name = project_name, - description = 'A library for running inference on a DeepSpeech model', - long_description = read('../../README.md'), - long_description_content_type = 'text/markdown; charset=UTF-8', - author = 'Mozilla', - version = project_version, - package_dir = {'deepspeech': '.'}, - cmdclass = {'build': BuildExtFirst}, - license = 'MPL-2.0', - url = 'https://github.com/mozilla/DeepSpeech', - project_urls = { - 'Documentation': 'https://github.com/mozilla/DeepSpeech/tree/v{}#project-deepspeech'.format(project_version), - 'Tracker': 'https://github.com/mozilla/DeepSpeech/issues', - 'Repository': 'https://github.com/mozilla/DeepSpeech/tree/v{}'.format(project_version), - 'Discussions': 'https://discourse.mozilla.org/c/deep-speech', - }, - ext_modules = [ds_ext], - py_modules = ['deepspeech', 'deepspeech.client', 'deepspeech.impl'], - entry_points={'console_scripts':['deepspeech = deepspeech.client:main']}, - install_requires = ['numpy%s' % numpy_min_ver], - include_package_data = True, - classifiers = [ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Topic :: Multimedia :: Sound/Audio :: Speech', - 'Topic :: Scientific/Engineering :: Human Machine Interfaces', - 'Topic :: Scientific/Engineering', - 'Topic :: Utilities', - ]) + raise AssertionError('os.name == java not expected') + + ds_ext = Extension(name='deepspeech._impl', + sources=['impl.i'], + include_dirs=[numpy_include, '../'], + library_dirs=list(map(lambda x: x.strip(), lib_dirs_split(os.getenv('MODEL_LDFLAGS', '')))), + libraries=list(map(lambda x: x.strip(), libs_split(os.getenv('MODEL_LIBS', '')))), + swig_opts=['-c++', '-keyword', '-builtin']) + + setup(name=project_name, + description='A library for running inference on a DeepSpeech model', + long_description=read('../../README.md'), + long_description_content_type='text/markdown; charset=UTF-8', + author='Mozilla', + version=project_version, + package_dir={'deepspeech': '.'}, + cmdclass={'build': BuildExtFirst}, + license='MPL-2.0', + url='https://github.com/mozilla/DeepSpeech', + project_urls={ + 'Documentation': 'https://github.com/mozilla/DeepSpeech/tree/v{}#project-deepspeech'.format(project_version), + 'Tracker': 'https://github.com/mozilla/DeepSpeech/issues', + 'Repository': 'https://github.com/mozilla/DeepSpeech/tree/v{}'.format(project_version), + 'Discussions': 'https://discourse.mozilla.org/c/deep-speech', + }, + ext_modules=[ds_ext], + py_modules=['deepspeech', 'deepspeech.client', 'deepspeech.impl'], + entry_points={'console_scripts':['deepspeech=deepspeech.client:main']}, + install_requires=['numpy%s' % numpy_min_ver], + include_package_data=True, + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Multimedia :: Sound/Audio :: Speech', + 'Topic :: Scientific/Engineering :: Human Machine Interfaces', + 'Topic :: Scientific/Engineering', + 'Topic :: Utilities', + ]) + +if __name__ == '__main__': + main()