├── .gitignore ├── COPYING ├── Makefile.am ├── README.md ├── bootstrap ├── conf └── res_speech_vosk.conf ├── configure.ac └── res-speech-vosk ├── Makefile.am └── res_speech_vosk.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.so 4 | *.lo 5 | *.a 6 | *.la 7 | *.loT 8 | *.orig 9 | *.rej 10 | .libs 11 | .deps 12 | 13 | # / 14 | /configure 15 | /config.log 16 | /config.nice 17 | /config.status 18 | /autom4te.cache 19 | /aclocal.m4 20 | /libtool 21 | /Makefile.in 22 | /Makefile 23 | /config.h 24 | /stamp-h1 25 | 26 | # /res-speech-vosk/ 27 | /res-speech-vosk/.libs 28 | /res-speech-vosk/.deps 29 | /res-speech-vosk/Makefile.in 30 | /res-speech-vosk/Makefile 31 | 32 | compile 33 | config.guess 34 | config.h.in 35 | config.sub 36 | depcomp 37 | install-sh 38 | ltmain.sh 39 | m4/ 40 | missing -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | GNU GENERAL PUBLIC LICENSE 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | Preamble 11 | 12 | The licenses for most software are designed to take away your 13 | freedom to share and change it. By contrast, the GNU General Public 14 | License is intended to guarantee your freedom to share and change free 15 | software--to make sure the software is free for all its users. This 16 | General Public License applies to most of the Free Software 17 | Foundation's software and to any other program whose authors commit to 18 | using it. (Some other Free Software Foundation software is covered by 19 | the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | this service if you wish), that you receive source code or can get it 26 | if you want it, that you can change the software or use pieces of it 27 | in new free programs; and that you know you can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid 30 | anyone to deny you these rights or to ask you to surrender the rights. 31 | These restrictions translate to certain responsibilities for you if you 32 | distribute copies of the software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must give the recipients all the rights that 36 | you have. You must make sure that they, too, receive or can get the 37 | source code. And you must show them these terms so they know their 38 | rights. 39 | 40 | We protect your rights with two steps: (1) copyright the software, and 41 | (2) offer you this license which gives you legal permission to copy, 42 | distribute and/or modify the software. 43 | 44 | Also, for each author's protection and ours, we want to make certain 45 | that everyone understands that there is no warranty for this free 46 | software. If the software is modified by someone else and passed on, we 47 | want its recipients to know that what they have is not the original, so 48 | that any problems introduced by others will not reflect on the original 49 | authors' reputations. 50 | 51 | Finally, any free program is threatened constantly by software 52 | patents. We wish to avoid the danger that redistributors of a free 53 | program will individually obtain patent licenses, in effect making the 54 | program proprietary. To prevent this, we have made it clear that any 55 | patent must be licensed for everyone's free use or not licensed at all. 56 | 57 | The precise terms and conditions for copying, distribution and 58 | modification follow. 59 | 60 | GNU GENERAL PUBLIC LICENSE 61 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 62 | 63 | 0. This License applies to any program or other work which contains 64 | a notice placed by the copyright holder saying it may be distributed 65 | under the terms of this General Public License. The "Program", below, 66 | refers to any such program or work, and a "work based on the Program" 67 | means either the Program or any derivative work under copyright law: 68 | that is to say, a work containing the Program or a portion of it, 69 | either verbatim or with modifications and/or translated into another 70 | language. (Hereinafter, translation is included without limitation in 71 | the term "modification".) Each licensee is addressed as "you". 72 | 73 | Activities other than copying, distribution and modification are not 74 | covered by this License; they are outside its scope. The act of 75 | running the Program is not restricted, and the output from the Program 76 | is covered only if its contents constitute a work based on the 77 | Program (independent of having been made by running the Program). 78 | Whether that is true depends on what the Program does. 79 | 80 | 1. You may copy and distribute verbatim copies of the Program's 81 | source code as you receive it, in any medium, provided that you 82 | conspicuously and appropriately publish on each copy an appropriate 83 | copyright notice and disclaimer of warranty; keep intact all the 84 | notices that refer to this License and to the absence of any warranty; 85 | and give any other recipients of the Program a copy of this License 86 | along with the Program. 87 | 88 | You may charge a fee for the physical act of transferring a copy, and 89 | you may at your option offer warranty protection in exchange for a fee. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion 92 | of it, thus forming a work based on the Program, and copy and 93 | distribute such modifications or work under the terms of Section 1 94 | above, provided that you also meet all of these conditions: 95 | 96 | a) You must cause the modified files to carry prominent notices 97 | stating that you changed the files and the date of any change. 98 | 99 | b) You must cause any work that you distribute or publish, that in 100 | whole or in part contains or is derived from the Program or any 101 | part thereof, to be licensed as a whole at no charge to all third 102 | parties under the terms of this License. 103 | 104 | c) If the modified program normally reads commands interactively 105 | when run, you must cause it, when started running for such 106 | interactive use in the most ordinary way, to print or display an 107 | announcement including an appropriate copyright notice and a 108 | notice that there is no warranty (or else, saying that you provide 109 | a warranty) and that users may redistribute the program under 110 | these conditions, and telling the user how to view a copy of this 111 | License. (Exception: if the Program itself is interactive but 112 | does not normally print such an announcement, your work based on 113 | the Program is not required to print an announcement.) 114 | 115 | These requirements apply to the modified work as a whole. If 116 | identifiable sections of that work are not derived from the Program, 117 | and can be reasonably considered independent and separate works in 118 | themselves, then this License, and its terms, do not apply to those 119 | sections when you distribute them as separate works. But when you 120 | distribute the same sections as part of a whole which is a work based 121 | on the Program, the distribution of the whole must be on the terms of 122 | this License, whose permissions for other licensees extend to the 123 | entire whole, and thus to each and every part regardless of who wrote it. 124 | 125 | Thus, it is not the intent of this section to claim rights or contest 126 | your rights to work written entirely by you; rather, the intent is to 127 | exercise the right to control the distribution of derivative or 128 | collective works based on the Program. 129 | 130 | In addition, mere aggregation of another work not based on the Program 131 | with the Program (or with a work based on the Program) on a volume of 132 | a storage or distribution medium does not bring the other work under 133 | the scope of this License. 134 | 135 | 3. You may copy and distribute the Program (or a work based on it, 136 | under Section 2) in object code or executable form under the terms of 137 | Sections 1 and 2 above provided that you also do one of the following: 138 | 139 | a) Accompany it with the complete corresponding machine-readable 140 | source code, which must be distributed under the terms of Sections 141 | 1 and 2 above on a medium customarily used for software interchange; or, 142 | 143 | b) Accompany it with a written offer, valid for at least three 144 | years, to give any third party, for a charge no more than your 145 | cost of physically performing source distribution, a complete 146 | machine-readable copy of the corresponding source code, to be 147 | distributed under the terms of Sections 1 and 2 above on a medium 148 | customarily used for software interchange; or, 149 | 150 | c) Accompany it with the information you received as to the offer 151 | to distribute corresponding source code. (This alternative is 152 | allowed only for noncommercial distribution and only if you 153 | received the program in object code or executable form with such 154 | an offer, in accord with Subsection b above.) 155 | 156 | The source code for a work means the preferred form of the work for 157 | making modifications to it. For an executable work, complete source 158 | code means all the source code for all modules it contains, plus any 159 | associated interface definition files, plus the scripts used to 160 | control compilation and installation of the executable. However, as a 161 | special exception, the source code distributed need not include 162 | anything that is normally distributed (in either source or binary 163 | form) with the major components (compiler, kernel, and so on) of the 164 | operating system on which the executable runs, unless that component 165 | itself accompanies the executable. 166 | 167 | If distribution of executable or object code is made by offering 168 | access to copy from a designated place, then offering equivalent 169 | access to copy the source code from the same place counts as 170 | distribution of the source code, even though third parties are not 171 | compelled to copy the source along with the object code. 172 | 173 | 4. You may not copy, modify, sublicense, or distribute the Program 174 | except as expressly provided under this License. Any attempt 175 | otherwise to copy, modify, sublicense or distribute the Program is 176 | void, and will automatically terminate your rights under this License. 177 | However, parties who have received copies, or rights, from you under 178 | this License will not have their licenses terminated so long as such 179 | parties remain in full compliance. 180 | 181 | 5. You are not required to accept this License, since you have not 182 | signed it. However, nothing else grants you permission to modify or 183 | distribute the Program or its derivative works. These actions are 184 | prohibited by law if you do not accept this License. Therefore, by 185 | modifying or distributing the Program (or any work based on the 186 | Program), you indicate your acceptance of this License to do so, and 187 | all its terms and conditions for copying, distributing or modifying 188 | the Program or works based on it. 189 | 190 | 6. Each time you redistribute the Program (or any work based on the 191 | Program), the recipient automatically receives a license from the 192 | original licensor to copy, distribute or modify the Program subject to 193 | these terms and conditions. You may not impose any further 194 | restrictions on the recipients' exercise of the rights granted herein. 195 | You are not responsible for enforcing compliance by third parties to 196 | this License. 197 | 198 | 7. If, as a consequence of a court judgment or allegation of patent 199 | infringement or for any other reason (not limited to patent issues), 200 | conditions are imposed on you (whether by court order, agreement or 201 | otherwise) that contradict the conditions of this License, they do not 202 | excuse you from the conditions of this License. If you cannot 203 | distribute so as to satisfy simultaneously your obligations under this 204 | License and any other pertinent obligations, then as a consequence you 205 | may not distribute the Program at all. For example, if a patent 206 | license would not permit royalty-free redistribution of the Program by 207 | all those who receive copies directly or indirectly through you, then 208 | the only way you could satisfy both it and this License would be to 209 | refrain entirely from distribution of the Program. 210 | 211 | If any portion of this section is held invalid or unenforceable under 212 | any particular circumstance, the balance of the section is intended to 213 | apply and the section as a whole is intended to apply in other 214 | circumstances. 215 | 216 | It is not the purpose of this section to induce you to infringe any 217 | patents or other property right claims or to contest validity of any 218 | such claims; this section has the sole purpose of protecting the 219 | integrity of the free software distribution system, which is 220 | implemented by public license practices. Many people have made 221 | generous contributions to the wide range of software distributed 222 | through that system in reliance on consistent application of that 223 | system; it is up to the author/donor to decide if he or she is willing 224 | to distribute software through any other system and a licensee cannot 225 | impose that choice. 226 | 227 | This section is intended to make thoroughly clear what is believed to 228 | be a consequence of the rest of this License. 229 | 230 | 8. If the distribution and/or use of the Program is restricted in 231 | certain countries either by patents or by copyrighted interfaces, the 232 | original copyright holder who places the Program under this License 233 | may add an explicit geographical distribution limitation excluding 234 | those countries, so that distribution is permitted only in or among 235 | countries not thus excluded. In such case, this License incorporates 236 | the limitation as if written in the body of this License. 237 | 238 | 9. The Free Software Foundation may publish revised and/or new versions 239 | of the General Public License from time to time. Such new versions will 240 | be similar in spirit to the present version, but may differ in detail to 241 | address new problems or concerns. 242 | 243 | Each version is given a distinguishing version number. If the Program 244 | specifies a version number of this License which applies to it and "any 245 | later version", you have the option of following the terms and conditions 246 | either of that version or of any later version published by the Free 247 | Software Foundation. If the Program does not specify a version number of 248 | this License, you may choose any version ever published by the Free Software 249 | Foundation. 250 | 251 | 10. If you wish to incorporate parts of the Program into other free 252 | programs whose distribution conditions are different, write to the author 253 | to ask for permission. For software which is copyrighted by the Free 254 | Software Foundation, write to the Free Software Foundation; we sometimes 255 | make exceptions for this. Our decision will be guided by the two goals 256 | of preserving the free status of all derivatives of our free software and 257 | of promoting the sharing and reuse of software generally. 258 | 259 | NO WARRANTY 260 | 261 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 262 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 263 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 264 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 265 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 266 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 267 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 268 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 269 | REPAIR OR CORRECTION. 270 | 271 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 272 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 273 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 274 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 275 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 276 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 277 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 278 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 279 | POSSIBILITY OF SUCH DAMAGES. 280 | 281 | END OF TERMS AND CONDITIONS 282 | 283 | How to Apply These Terms to Your New Programs 284 | 285 | If you develop a new program, and you want it to be of the greatest 286 | possible use to the public, the best way to achieve this is to make it 287 | free software which everyone can redistribute and change under these terms. 288 | 289 | To do so, attach the following notices to the program. It is safest 290 | to attach them to the start of each source file to most effectively 291 | convey the exclusion of warranty; and each file should have at least 292 | the "copyright" line and a pointer to where the full notice is found. 293 | 294 | 295 | Copyright (C) 19yy 296 | 297 | This program is free software; you can redistribute it and/or modify 298 | it under the terms of the GNU General Public License as published by 299 | the Free Software Foundation; either version 2 of the License, or 300 | (at your option) any later version. 301 | 302 | This program is distributed in the hope that it will be useful, 303 | but WITHOUT ANY WARRANTY; without even the implied warranty of 304 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 305 | GNU General Public License for more details. 306 | 307 | You should have received a copy of the GNU General Public License 308 | along with this program; if not, write to the Free Software 309 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 310 | 311 | 312 | Also add information on how to contact you by electronic and paper mail. 313 | 314 | If the program is interactive, make it output a short notice like this 315 | when it starts in an interactive mode: 316 | 317 | Gnomovision version 69, Copyright (C) 19yy name of author 318 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 319 | This is free software, and you are welcome to redistribute it 320 | under certain conditions; type `show c' for details. 321 | 322 | The hypothetical commands `show w' and `show c' should show the appropriate 323 | parts of the General Public License. Of course, the commands you use may 324 | be called something other than `show w' and `show c'; they could even be 325 | mouse-clicks or menu items--whatever suits your program. 326 | 327 | You should also get your employer (if you work as a programmer) or your 328 | school, if any, to sign a "copyright disclaimer" for the program, if 329 | necessary. Here is a sample; alter the names: 330 | 331 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 332 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 333 | 334 | , 1 April 1989 335 | Ty Coon, President of Vice 336 | 337 | This General Public License does not permit incorporating your program into 338 | proprietary programs. If your program is a subroutine library, you may 339 | consider it more useful to permit linking proprietary applications with the 340 | library. If this is what you want to do, use the GNU Library General 341 | Public License instead of this License. 342 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure 3 | ACLOCAL_AMFLAGS = -I m4 4 | 5 | SUBDIRS = res-speech-vosk 6 | 7 | EXTRA_DIST = bootstrap conf/res_speech_vosk.conf 8 | 9 | install-data-local: 10 | test -d $(DESTDIR)$(ASTERISK_CONF_DIR) || $(mkinstalldirs) $(DESTDIR)$(ASTERISK_CONF_DIR) 11 | test -f $(DESTDIR)$(ASTERISK_CONF_DIR)/res_speech_vosk.conf || $(INSTALL) -m 644 $(top_srcdir)/conf/res_speech_vosk.conf $(DESTDIR)$(ASTERISK_CONF_DIR) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vosk speech recognition modules for Asterisk 2 | 3 | This is an asterisk module for [Vosk API](https://github.com/alphacep/vosk-api) server: 4 | 5 | https://github.com/alphacep/vosk-server 6 | 7 | It is tested with latest asterisk git master, but should equally work 8 | with other branches (13,16,17). 9 | 10 | 11 | ## Installation 12 | 13 | 1) Make sure you have latest asterisk update 14 | 15 | ``` 16 | git clone https://github.com/asterisk/asterisk 17 | .... 18 | ``` 19 | 20 | 2) First build the modules 21 | 22 | ``` 23 | ./bootstrap 24 | ./configure --with-asterisk= --prefix= 25 | make 26 | make install 27 | ``` 28 | 29 | for example: 30 | 31 | ``` 32 | ./bootstrap 33 | ./configure --with-asterisk=/usr --prefix=/usr 34 | make 35 | make install 36 | ``` 37 | 38 | 3) Edit `modules.conf` to load modules 39 | 40 | ``` 41 | load = res_speech.so 42 | load = res_http_websocket.so 43 | load = res_speech_vosk.so 44 | ``` 45 | 46 | 4) Edit dialplan in `extensions.conf`: 47 | 48 | ``` 49 | [internal] 50 | exten = 1,1,Answer 51 | same = n,Wait(1) 52 | same = n,SpeechCreate 53 | same = n,SpeechBackground(hello) 54 | same = n,Verbose(0,Result was ${SPEECH_TEXT(0)}) 55 | ``` 56 | 57 | 5) Run Vosk server with the Docker 58 | 59 | ``` 60 | docker run -d -p 2700:2700 alphacep/kaldi-en:latest 61 | ``` 62 | 63 | 6) Dial extension and check the result 64 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -x 4 | libtoolize --force --automake --copy 5 | autoheader 6 | aclocal -I m4 7 | automake --foreign --add-missing --copy 8 | autoconf 9 | 10 | rm -rf autom4te.cache 11 | -------------------------------------------------------------------------------- /conf/res_speech_vosk.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | url = ws://localhost:2700 3 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(asterisk-vosk, 0.3.7) 2 | AM_INIT_AUTOMAKE([no-define foreign nostdinc]) 3 | AC_CONFIG_MACRO_DIR([m4]) 4 | AC_CANONICAL_HOST 5 | AC_PROG_CC 6 | AC_PROG_INSTALL 7 | AC_CONFIG_HEADERS([config.h]) 8 | 9 | LT_INIT 10 | 11 | AC_ARG_WITH([asterisk], 12 | [--with-asterisk=DIR specify location of Asterisk], 13 | [asterisk_dir=$withval], 14 | [asterisk_dir="/opt"]) 15 | 16 | asterisk_include_dir="${asterisk_dir}/include" 17 | dnl Test Asterisk include directory and header file. 18 | if test -f "$asterisk_include_dir/asterisk.h"; then 19 | if test -d "$asterisk_include_dir/asterisk"; then 20 | ASTERISK_INCLUDES="-I$asterisk_include_dir" 21 | else 22 | AC_MSG_ERROR([Could not find Asterisk include directory, make sure Asterisk development package is installed]) 23 | fi 24 | else 25 | AC_MSG_ERROR([Could not find asterisk.h, make sure Asterisk development package is installed]) 26 | fi 27 | ASTERISK_MODDIR="${prefix}/lib/asterisk/modules" 28 | ASTERISK_CONF_DIR="${prefix}/etc/asterisk" 29 | AC_SUBST(ASTERISK_INCLUDES) 30 | AC_SUBST(ASTERISK_MODDIR) 31 | AC_SUBST(ASTERISK_CONF_DIR) 32 | 33 | AC_CONFIG_FILES([ 34 | Makefile 35 | res-speech-vosk/Makefile 36 | ]) 37 | 38 | AC_OUTPUT 39 | -------------------------------------------------------------------------------- /res-speech-vosk/Makefile.am: -------------------------------------------------------------------------------- 1 | MAINTAINERCLEANFILES = Makefile.in 2 | 3 | AM_CPPFLAGS = -I$(top_srcdir)/include $(ASTERISK_INCLUDES) 4 | AM_CFLAGS = -DAST_MODULE_SELF_SYM="__internal_res_speech_vosk" 5 | 6 | moddir = $(ASTERISK_MODDIR) 7 | mod_LTLIBRARIES = res_speech_vosk.la 8 | 9 | res_speech_vosk_la_SOURCES = res_speech_vosk.c 10 | res_speech_vosk_la_LDFLAGS = -avoid-version -no-undefined -module 11 | res_speech_vosk_la_LIBADD = $(VOSK_LIBS) 12 | 13 | load: 14 | asterisk -rx "module load res_speech_vosk.so" 15 | 16 | unload: 17 | asterisk -rx "module unload res_speech_vosk.so" 18 | -------------------------------------------------------------------------------- /res-speech-vosk/res_speech_vosk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk -- An open source telephony toolkit. 3 | * 4 | * See http://www.asterisk.org for more information about 5 | * the Asterisk project. Please do not directly contact 6 | * any of the maintainers of this project for assistance; 7 | * the project provides a web site, mailing lists and IRC 8 | * channels for your use. 9 | * 10 | * This program is free software, distributed under the terms of 11 | * the GNU General Public License Version 2. See the LICENSE file 12 | * at the top of the source tree. 13 | * 14 | * Please follow coding guidelines 15 | * http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES 16 | */ 17 | 18 | /*! \file 19 | * 20 | * \brief Implementation of the Asterisk's Speech API via Vosk 21 | * 22 | * \author Nickolay V. Shmyrev 23 | * 24 | * \ingroup applications 25 | */ 26 | 27 | /* Asterisk includes. */ 28 | #include "asterisk.h" 29 | #include "asterisk/logger.h" 30 | #include "asterisk/channel.h" 31 | 32 | #define AST_MODULE "res_speech_vosk" 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | #define VOSK_ENGINE_NAME "vosk" 44 | #define VOSK_ENGINE_CONFIG "res_speech_vosk.conf" 45 | #define VOSK_BUF_SIZE 3200 46 | 47 | /** \brief Forward declaration of speech (client object) */ 48 | typedef struct vosk_speech_t vosk_speech_t; 49 | /** \brief Forward declaration of engine (global object) */ 50 | typedef struct vosk_engine_t vosk_engine_t; 51 | 52 | /** \brief Declaration of Vosk speech structure */ 53 | struct vosk_speech_t { 54 | /* Name of the speech object to be used for logging */ 55 | char *name; 56 | /* Websocket connection */ 57 | struct ast_websocket *ws; 58 | /* Buffer for frames */ 59 | char buf[VOSK_BUF_SIZE]; 60 | int offset; 61 | char *last_result; 62 | }; 63 | 64 | /** \brief Declaration of Vosk recognition engine */ 65 | struct vosk_engine_t { 66 | /* Websocket url*/ 67 | char *ws_url; 68 | }; 69 | 70 | static struct vosk_engine_t vosk_engine; 71 | 72 | /** \brief Set up the speech structure within the engine */ 73 | static int vosk_recog_create(struct ast_speech *speech, struct ast_format *format) 74 | { 75 | vosk_speech_t *vosk_speech; 76 | enum ast_websocket_result result; 77 | 78 | vosk_speech = ast_calloc(1, sizeof(vosk_speech_t)); 79 | vosk_speech->name = "vosk"; 80 | speech->data = vosk_speech; 81 | 82 | ast_debug(1, "(%s) Create speech resource %s\n",vosk_speech->name, vosk_engine.ws_url); 83 | 84 | vosk_speech->ws = ast_websocket_client_create(vosk_engine.ws_url, "ws", NULL, &result); 85 | if (!vosk_speech->ws) { 86 | ast_free(speech->data); 87 | return -1; 88 | } 89 | 90 | ast_debug(1, "(%s) Created speech resource result %d\n", vosk_speech->name, result); 91 | 92 | return 0; 93 | } 94 | 95 | /** \brief Destroy any data set on the speech structure by the engine */ 96 | static int vosk_recog_destroy(struct ast_speech *speech) 97 | { 98 | const char *eof = "{\"eof\" : 1}"; 99 | 100 | vosk_speech_t *vosk_speech = speech->data; 101 | ast_debug(1, "(%s) Destroy speech resource\n",vosk_speech->name); 102 | 103 | if (vosk_speech->ws) { 104 | int fd = ast_websocket_fd(vosk_speech->ws); 105 | if (fd > 0) { 106 | ast_websocket_write_string(vosk_speech->ws, eof); 107 | ast_websocket_close(vosk_speech->ws, 1000); 108 | shutdown(fd, SHUT_RDWR); 109 | } 110 | ast_websocket_unref(vosk_speech->ws); 111 | } 112 | ast_free(vosk_speech->last_result); 113 | ast_free(vosk_speech); 114 | 115 | return 0; 116 | } 117 | 118 | /*! \brief Stop the in-progress recognition */ 119 | static int vosk_recog_stop(struct ast_speech *speech) 120 | { 121 | vosk_speech_t *vosk_speech = speech->data; 122 | ast_debug(1, "(%s) Stop recognition\n",vosk_speech->name); 123 | ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY); 124 | return 0; 125 | } 126 | 127 | /*! \brief Load a local grammar on the speech structure */ 128 | static int vosk_recog_load_grammar(struct ast_speech *speech, const char *grammar_name, const char *grammar_path) 129 | { 130 | return 0; 131 | } 132 | 133 | /** \brief Unload a local grammar */ 134 | static int vosk_recog_unload_grammar(struct ast_speech *speech, const char *grammar_name) 135 | { 136 | return 0; 137 | } 138 | 139 | /** \brief Activate a loaded grammar */ 140 | static int vosk_recog_activate_grammar(struct ast_speech *speech, const char *grammar_name) 141 | { 142 | return 0; 143 | } 144 | 145 | /** \brief Deactivate a loaded grammar */ 146 | static int vosk_recog_deactivate_grammar(struct ast_speech *speech, const char *grammar_name) 147 | { 148 | return 0; 149 | } 150 | 151 | /** \brief Write audio to the speech engine */ 152 | static int vosk_recog_write(struct ast_speech *speech, void *data, int len) 153 | { 154 | vosk_speech_t *vosk_speech = speech->data; 155 | char *res; 156 | int res_len; 157 | 158 | ast_assert (vosk_speech->offset + len < VOSK_BUF_SIZE); 159 | 160 | memcpy(vosk_speech->buf + vosk_speech->offset, data, len); 161 | vosk_speech->offset += len; 162 | if (vosk_speech->offset == VOSK_BUF_SIZE) { 163 | ast_websocket_write(vosk_speech->ws, AST_WEBSOCKET_OPCODE_BINARY, vosk_speech->buf, VOSK_BUF_SIZE); 164 | vosk_speech->offset = 0; 165 | } 166 | 167 | ast_mutex_unlock(&speech->lock); 168 | if (ast_websocket_wait_for_input(vosk_speech->ws, 0) > 0) { 169 | res_len = ast_websocket_read_string(vosk_speech->ws, &res); 170 | if (res_len >= 0) { 171 | ast_verb(4, "(%s) Got result: '%s'\n", vosk_speech->name, res); 172 | struct ast_json_error err; 173 | struct ast_json *res_json = ast_json_load_string(res, &err); 174 | if (res_json != NULL) { 175 | const char *text = ast_json_object_string_get(res_json, "text"); 176 | const char *partial = ast_json_object_string_get(res_json, "partial"); 177 | if (partial != NULL && !ast_strlen_zero(partial)) { 178 | ast_verb(4, "(%s) Partial recognition result: %s\n", vosk_speech->name, partial); 179 | ast_free(vosk_speech->last_result); 180 | vosk_speech->last_result = ast_strdup(partial); 181 | } else if (text != NULL && !ast_strlen_zero(text)) { 182 | ast_verb(4, "(%s) Recognition result: %s\n", vosk_speech->name, text); 183 | ast_free(vosk_speech->last_result); 184 | vosk_speech->last_result = ast_strdup(text); 185 | ast_speech_change_state(speech, AST_SPEECH_STATE_DONE); 186 | } 187 | } else { 188 | ast_log(LOG_ERROR, "(%s) JSON parse error: %s\n", vosk_speech->name, err.text); 189 | } 190 | ast_json_free(res_json); 191 | } else { 192 | ast_log(LOG_NOTICE, "(%s) Got error result %d\n", vosk_speech->name, res_len); 193 | } 194 | } 195 | ast_mutex_lock(&speech->lock); 196 | 197 | return 0; 198 | } 199 | 200 | /** \brief Signal DTMF was received */ 201 | static int vosk_recog_dtmf(struct ast_speech *speech, const char *dtmf) 202 | { 203 | vosk_speech_t *vosk_speech = speech->data; 204 | ast_verb(4, "(%s) Signal DTMF %s\n",vosk_speech->name,dtmf); 205 | return 0; 206 | } 207 | 208 | /** brief Prepare engine to accept audio */ 209 | static int vosk_recog_start(struct ast_speech *speech) 210 | { 211 | vosk_speech_t *vosk_speech = speech->data; 212 | ast_debug(1, "(%s) Start recognition\n",vosk_speech->name); 213 | ast_speech_change_state(speech, AST_SPEECH_STATE_READY); 214 | return 0; 215 | } 216 | 217 | /** \brief Change an engine specific setting */ 218 | static int vosk_recog_change(struct ast_speech *speech, const char *name, const char *value) 219 | { 220 | vosk_speech_t *vosk_speech = speech->data; 221 | ast_debug(1, "(%s) Change setting name: %s value:%s\n",vosk_speech->name,name,value); 222 | return 0; 223 | } 224 | 225 | /** \brief Get an engine specific attribute */ 226 | static int vosk_recog_get_settings(struct ast_speech *speech, const char *name, char *buf, size_t len) 227 | { 228 | vosk_speech_t *vosk_speech = speech->data; 229 | ast_debug(1, "(%s) Get settings name: %s\n",vosk_speech->name,name); 230 | return -1; 231 | } 232 | 233 | /** \brief Change the type of results we want back */ 234 | static int vosk_recog_change_results_type(struct ast_speech *speech,enum ast_speech_results_type results_type) 235 | { 236 | return -1; 237 | } 238 | 239 | /** \brief Try to get result */ 240 | struct ast_speech_result* vosk_recog_get(struct ast_speech *speech) 241 | { 242 | struct ast_speech_result *speech_result; 243 | 244 | vosk_speech_t *vosk_speech = speech->data; 245 | speech_result = ast_calloc(sizeof(struct ast_speech_result), 1); 246 | speech_result->text = ast_strdup(vosk_speech->last_result); 247 | speech_result->score = 100; 248 | 249 | ast_set_flag(speech, AST_SPEECH_HAVE_RESULTS); 250 | return speech_result; 251 | } 252 | 253 | /** \brief Speech engine declaration */ 254 | static struct ast_speech_engine ast_engine = { 255 | VOSK_ENGINE_NAME, 256 | vosk_recog_create, 257 | vosk_recog_destroy, 258 | vosk_recog_load_grammar, 259 | vosk_recog_unload_grammar, 260 | vosk_recog_activate_grammar, 261 | vosk_recog_deactivate_grammar, 262 | vosk_recog_write, 263 | vosk_recog_dtmf, 264 | vosk_recog_start, 265 | vosk_recog_change, 266 | vosk_recog_get_settings, 267 | vosk_recog_change_results_type, 268 | vosk_recog_get 269 | }; 270 | 271 | /** \brief Load Vosk engine configuration (/etc/asterisk/res_speech_vosk.conf)*/ 272 | static int vosk_engine_config_load() 273 | { 274 | const char *value = NULL; 275 | struct ast_flags config_flags = { 0 }; 276 | struct ast_config *cfg = ast_config_load(VOSK_ENGINE_CONFIG, config_flags); 277 | if(!cfg) { 278 | ast_log(LOG_WARNING, "No such configuration file %s\n", VOSK_ENGINE_CONFIG); 279 | return -1; 280 | } 281 | if((value = ast_variable_retrieve(cfg, "general", "url")) != NULL) { 282 | ast_log(LOG_DEBUG, "general.url=%s\n", value); 283 | vosk_engine.ws_url = ast_strdup(value); 284 | } 285 | if (!vosk_engine.ws_url) { 286 | vosk_engine.ws_url = ast_strdup("ws://localhost"); 287 | } 288 | ast_config_destroy(cfg); 289 | return 0; 290 | } 291 | 292 | /** \brief Load module */ 293 | static int load_module(void) 294 | { 295 | ast_log(LOG_NOTICE, "Load res_speech_vosk module\n"); 296 | 297 | /* Load engine configuration */ 298 | if (vosk_engine_config_load()) { 299 | return AST_MODULE_LOAD_DECLINE; 300 | } 301 | 302 | ast_engine.formats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); 303 | if(!ast_engine.formats) { 304 | ast_log(LOG_ERROR, "Failed to alloc media format capabilities\n"); 305 | return AST_MODULE_LOAD_FAILURE; 306 | } 307 | ast_format_cap_append(ast_engine.formats, ast_format_slin, 0); 308 | 309 | if(ast_speech_register(&ast_engine)) { 310 | ast_log(LOG_ERROR, "Failed to register module\n"); 311 | return AST_MODULE_LOAD_FAILURE; 312 | } 313 | 314 | return AST_MODULE_LOAD_SUCCESS; 315 | } 316 | 317 | /** \brief Unload module */ 318 | static int unload_module(void) 319 | { 320 | ast_log(LOG_NOTICE, "Unload res_speech_vosk module\n"); 321 | if(ast_speech_unregister(VOSK_ENGINE_NAME)) { 322 | ast_log(LOG_ERROR, "Failed to unregister module\n"); 323 | } 324 | 325 | ast_free(vosk_engine.ws_url); 326 | return 0; 327 | } 328 | 329 | AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Vosk Speech Engine"); 330 | --------------------------------------------------------------------------------