├── .github └── workflows │ └── docker-image.yml ├── Dockerfile ├── INSTALL ├── README.md ├── asterisk_modules ├── app_grpcsttbackground │ ├── AUTHORS │ ├── COPYING │ ├── ChangeLog │ ├── INSTALL │ ├── Makefile.am │ ├── NEWS │ ├── README │ ├── app_grpcsttbackground.c │ ├── awk_get_documentation │ ├── bootstrap │ ├── compile │ ├── config.guess │ ├── config.sub │ ├── configure.ac │ ├── depcomp │ ├── grpc_stt.cpp │ ├── grpc_stt.h │ ├── install-sh │ ├── jwt.cpp │ ├── jwt.h │ ├── missing │ ├── samples │ │ └── grpcstt.conf.sample │ └── stt.proto ├── app_playbackground │ ├── AUTHORS │ ├── COPYING │ ├── ChangeLog │ ├── INSTALL │ ├── Makefile.am │ ├── NEWS │ ├── RAII.h │ ├── README │ ├── app_playbackground.c │ ├── awk_get_documentation │ ├── bootstrap │ ├── bytequeue.cpp │ ├── bytequeue.h │ ├── channel.cpp │ ├── channel.h │ ├── channelbackend.cpp │ ├── channelbackend.h │ ├── compile │ ├── config.guess │ ├── config.sub │ ├── configure.ac │ ├── depcomp │ ├── grpctts.cpp │ ├── grpctts.h │ ├── grpctts_conf.c │ ├── grpctts_conf.h │ ├── install-sh │ ├── job.cpp │ ├── job.h │ ├── jwt.cpp │ ├── jwt.h │ ├── missing │ ├── samples │ │ └── grpctts.conf.sample │ ├── stream_layers.c │ ├── stream_layers.h │ └── tts.proto ├── app_waitevent │ ├── AUTHORS │ ├── COPYING │ ├── ChangeLog │ ├── INSTALL │ ├── Makefile.am │ ├── NEWS │ ├── README │ ├── app_waitevent.c │ ├── awk_get_documentation │ ├── bootstrap │ ├── compile │ ├── config.guess │ ├── config.sub │ ├── configure.ac │ ├── depcomp │ ├── install-sh │ └── missing ├── bootstrap ├── func_gettimensec │ ├── AUTHORS │ ├── COPYING │ ├── ChangeLog │ ├── INSTALL │ ├── Makefile.am │ ├── NEWS │ ├── README │ ├── awk_get_documentation │ ├── bootstrap │ ├── compile │ ├── config.guess │ ├── config.sub │ ├── configure.ac │ ├── depcomp │ ├── func_gettimensec.c │ ├── install-sh │ └── missing └── thirdparty │ ├── Makefile │ ├── bootstrap │ ├── grpc-build.patch │ ├── grpc-gettid.patch │ ├── grpc-pollset-fix.patch │ └── grpc-transport_fd_pipe.patch ├── container-data └── extract_text ├── container-mnt ├── extensions.ael ├── grpcstt.conf ├── grpctts.conf └── sip.conf └── examples ├── combined └── combined_stt_tts │ └── etc │ └── asterisk │ ├── extensions.ael │ ├── grpcstt.conf │ └── grpctts.conf ├── stt ├── stt_basic │ └── etc │ │ └── asterisk │ │ ├── extensions.ael │ │ └── grpcstt.conf ├── stt_detailed │ └── etc │ │ └── asterisk │ │ ├── extensions.ael │ │ └── grpcstt.conf ├── stt_error_handling │ └── etc │ │ └── asterisk │ │ ├── extensions.ael │ │ └── grpcstt.conf ├── stt_with_playback │ └── etc │ │ └── asterisk │ │ ├── extensions.ael │ │ └── grpcstt.conf ├── stt_with_playback_advanced │ └── etc │ │ └── asterisk │ │ ├── extensions.ael │ │ └── grpcstt.conf └── stt_with_playback_ami │ ├── README │ ├── ami-request.sh │ └── etc │ └── asterisk │ ├── extensions.ael │ └── grpcstt.conf └── tts ├── tts_basic └── etc │ └── asterisk │ ├── extensions.ael │ └── grpctts.conf ├── tts_event_driven └── etc │ └── asterisk │ ├── extensions.ael │ └── grpctts.conf ├── tts_event_driven_ami ├── README ├── ami-request.sh └── etc │ └── asterisk │ ├── extensions.ael │ └── grpctts.conf └── tts_wait_for_event └── etc └── asterisk ├── extensions.ael └── grpctts.conf /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Basic Docker Image with Asterisk VoiceKit modules 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Login to Docker 17 | run: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} 18 | - name: Build and push Docker image 19 | run: | 20 | IMAGE_VERSION=$(git describe --tags) 21 | docker build . --tag tinkoffcreditsystems/asterisk-voicekit-modules:"${IMAGE_VERSION}" 22 | docker push tinkoffcreditsystems/asterisk-voicekit-modules:"${IMAGE_VERSION}" 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | 5 | RUN apt-get update 6 | RUN apt-get install -y git g++ libtool autoconf automake m4 make 7 | RUN apt-get install -y asterisk-dev libopus-dev 8 | RUN apt-get install -y curl xxd jq libssl-dev pkg-config 9 | 10 | RUN mkdir -p /usr/src/asterisk-voicekit-modules 11 | 12 | COPY .git /usr/src/asterisk-voicekit-modules/.git/ 13 | 14 | RUN cd /usr/src/asterisk-voicekit-modules && git checkout . 15 | # NOTE: Alternatively use following (instead of previous line) to apply local modifications: 16 | # COPY asterisk_modules /usr/src/asterisk-voicekit-modules/asterisk_modules/ 17 | 18 | RUN cd /usr/src/asterisk-voicekit-modules/asterisk_modules && ./bootstrap 19 | RUN cd /usr/src/asterisk-voicekit-modules/asterisk_modules/app_grpcsttbackground && \ 20 | ./configure --prefix=/usr --localstatedir=/var --with-asterisk-xmldoc-dir=/usr/share/asterisk/documentation && \ 21 | make && \ 22 | make install 23 | RUN cd /usr/src/asterisk-voicekit-modules/asterisk_modules/app_playbackground && \ 24 | ./configure --prefix=/usr --localstatedir=/var --with-asterisk-xmldoc-dir=/usr/share/asterisk/documentation && \ 25 | make && \ 26 | make install 27 | RUN cd /usr/src/asterisk-voicekit-modules/asterisk_modules/app_waitevent && \ 28 | ./configure --prefix=/usr --localstatedir=/var --with-asterisk-xmldoc-dir=/usr/share/asterisk/documentation && \ 29 | make && \ 30 | make install 31 | RUN cd /usr/src/asterisk-voicekit-modules/asterisk_modules/func_gettimensec && \ 32 | ./configure --prefix=/usr --localstatedir=/var --with-asterisk-xmldoc-dir=/usr/share/asterisk/documentation && \ 33 | make && \ 34 | make install 35 | 36 | COPY container-data/extract_text /usr/share/asterisk/agi-bin/extract_text 37 | RUN chmod 755 /usr/share/asterisk/agi-bin/extract_text 38 | 39 | RUN rm -f /etc/asterisk/extensions.ael /etc/asterisk/sip.conf 40 | RUN ln -s /mnt/extensions.ael /etc/asterisk/extensions.ael 41 | RUN ln -s /mnt/grpcstt.conf /etc/asterisk/grpcstt.conf 42 | RUN ln -s /mnt/grpctts.conf /etc/asterisk/grpctts.conf 43 | RUN ln -s /mnt/sip.conf /etc/asterisk/sip.conf 44 | 45 | CMD asterisk -c 46 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | Install dependencies into system: 5 | 6 | ``` 7 | sudo apt-get install asterisk-dev libopus-dev 8 | ``` 9 | 10 | 11 | Call bootstrap for whole repository: 12 | 13 | ``` 14 | cd asterisk_modules 15 | ./bootstrap 16 | ``` 17 | 18 | 19 | Now to build each module see INSTALL files at corresponding directory. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | INTRODUCTION 2 | ------------ 3 | 4 | This document provides a brief description of the VoiceKit modules for Asterisk. 5 | Each module may be used individually although there are shared thitd party repositories. 6 | 7 | Following presentation may be helpful for Russian speakers as an introduction: 8 | https://www.youtube.com/watch?v=4LRcaSM46t8 9 | 10 | 11 | INSTALLATION 12 | ------------ 13 | 14 | See INSTALL files for corresponding modules. 15 | 16 | 17 | QUICKSTART 18 | ---------- 19 | 20 | See 'examples' directory for dial-plan and configuration examples. 21 | 22 | You may also build Docker image or use pre-built one with STT and TTS demo using your API credentials. 23 | 24 | Building demo Docker image 25 | ========================== 26 | 27 | 1. Put your API credentials into `api_key` and `secret_key` variables at `container-mnt/grpcstt.conf` and `container-mnt/grpcstt.conf` 28 | 29 | 2. Build image: 30 | 31 | ``` 32 | docker build -t asterisk-voicekit-demo . 33 | ``` 34 | 35 | 3. Run container in foreground mode: 36 | 37 | ``` 38 | docker run -it --network=host --mount type=bind,src="$PWD/container-mnt",dst=/mnt asterisk-voicekit-demo 39 | ``` 40 | 41 | **NOTE**: System-wide Asterisk must be stopped if running! 42 | 43 | Using pre-built Docker image 44 | ============================ 45 | 46 | 1. Put your API credentials into `api_key` and `secret_key` variables at `container-mnt/grpcstt.conf` and `container-mnt/grpcstt.conf` 47 | 48 | 2. Run container in foreground mode: 49 | 50 | ``` 51 | docker run -it --network=host --mount type=bind,src="$PWD/container-mnt",dst=/mnt tinkoffcreditsystems/asterisk-voicekit-modules:`git describe --tags` 52 | ``` 53 | 54 | **NOTE**: System-wide Asterisk must be stopped if running! 55 | 56 | **NOTE**: The modules repository must be at recent release tag for `git describe --tags` to work properly! 57 | 58 | For full list of pre-built images see: https://hub.docker.com/r/tinkoffcreditsystems/asterisk-voicekit-modules/tags 59 | 60 | Configuring SIP client 61 | ====================== 62 | 63 | 1. Install Ekiga: 64 | 65 | ``` 66 | sudo apt-get install ekiga 67 | ``` 68 | 69 | 2. Configure Ekiga: 70 | 71 | - Set "Edit -> Preferences -> Protocols -> SIP settings -> Outbound proxy" to "localhost" 72 | - Add account at "Edit -> Accounts" with following settings: 73 | - Name: `demo-user` 74 | - Registrar: `sip` 75 | - User: `demo-user` 76 | - Authentication user: `demo-user` 77 | - Password: `lBwzDjXAwMs94Sn` 78 | - Add contact at "Chat -> Add contact" with following settings: 79 | - Name: `Voicekit parrot` 80 | - Address: `sip:parrot` 81 | 82 | 3. Call contact `Voicekit parrot` 83 | 84 | LICENSING 85 | --------- 86 | 87 | Since Asterisk is distributed under the GPLv2 license, and the VoiceKit modules are loaded by and 88 | directly interface with Asterisk, the GPLv2 license applies to the VoiceKit modules too. 89 | 90 | See COPYING files for corresponding modules for details. 91 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/AUTHORS: -------------------------------------------------------------------------------- 1 | Grigoriy Okopnik 2 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/app_grpcsttbackground/ChangeLog -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/INSTALL: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | ``` 5 | ./configure --prefix=PREFIX 6 | make 7 | make install-strip 8 | ``` 9 | 10 | 11 | Dependencies 12 | ============ 13 | 14 | GRPC from https://github.com/grpc/grpc 15 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/Makefile.am: -------------------------------------------------------------------------------- 1 | pkgdir = $(asteriskmoduledir) 2 | pkg_LTLIBRARIES = app_grpcsttbackground.la 3 | PROTO_BUILT_SOURCES = \ 4 | stt.pb.cc stt.pb.h \ 5 | stt.grpc.pb.cc stt.grpc.pb.h \ 6 | google/api/annotations.pb.cc google/api/annotations.pb.h \ 7 | google/api/http.pb.cc google/api/http.pb.h 8 | BUILT_SOURCES = ../thirdparty/inst/include/grpc/grpc.h ../thirdparty/inst/lib/libjansson.a roots.pem.h $(PROTO_BUILT_SOURCES) 9 | 10 | app_grpcsttbackground_la_SOURCES = \ 11 | app_grpcsttbackground.c \ 12 | grpc_stt.cpp \ 13 | jwt.cpp \ 14 | $(PROTO_BUILT_SOURCES) 15 | app_grpcsttbackground_la_CFLAGS = -Wall -O3 -Werror=implicit-function-declaration -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -I../thirdparty/inst/include \ 16 | -fPIC -DAST_MODULE=\"app_grpcsttbackground\" -DASTERISK_MODULE_VERSION_STRING=\"`git describe --tags --always`\" 17 | app_grpcsttbackground_la_CXXFLAGS = -Wall -O3 -std=c++11 -I../thirdparty/inst/include -fPIC 18 | app_grpcsttbackground_la_LDFLAGS = -Wl,-E -pthread -g -module -avoid-version -ldl -Wl,-fuse-ld=gold \ 19 | ../thirdparty/inst/lib/libjansson.a \ 20 | ../thirdparty/inst/lib/libprotobuf.a \ 21 | ../thirdparty/inst/lib/libprotoc.a \ 22 | ../thirdparty/inst/lib/libaddress_sorting.a \ 23 | ../thirdparty/inst/lib/libgpr.a \ 24 | ../thirdparty/inst/lib/libgrpc.a \ 25 | ../thirdparty/inst/lib/libgrpc++.a \ 26 | ../thirdparty/inst/lib/libgrpc_cronet.a \ 27 | ../thirdparty/inst/lib/libgrpc++_cronet.a \ 28 | ../thirdparty/inst/lib/libgrpc++_error_details.a \ 29 | ../thirdparty/inst/lib/libgrpc++_reflection.a \ 30 | ../thirdparty/inst/lib/libgrpc_unsecure.a \ 31 | ../thirdparty/inst/lib/libgrpc++_unsecure.a 32 | app_grpcsttbackground_la_LIBTOOLFLAGS = --tag=disable-static 33 | 34 | CLEANFILES=$(PROTO_BUILT_SOURCES) roots.pem.h 35 | 36 | 37 | stt.pb.cc stt.pb.h: stt.proto 38 | ../thirdparty/inst/bin/protoc -I . -I ../thirdparty/googleapis --cpp_out=. stt.proto 39 | 40 | google/api/annotations.pb.cc google/api/annotations.pb.h: ../thirdparty/googleapis/google/api/annotations.proto 41 | ../thirdparty/inst/bin/protoc -I ../thirdparty/googleapis --cpp_out=. ../thirdparty/googleapis/google/api/annotations.proto 42 | 43 | google/api/http.pb.cc google/api/http.pb.h: ../thirdparty/googleapis/google/api/http.proto 44 | ../thirdparty/inst/bin/protoc -I ../thirdparty/googleapis --cpp_out=. ../thirdparty/googleapis/google/api/http.proto 45 | 46 | stt.grpc.pb.cc stt.grpc.pb.h: stt.proto 47 | ../thirdparty/inst/bin/protoc -I . -I ../thirdparty/googleapis --grpc_out=. --plugin=protoc-gen-grpc=../thirdparty/inst/bin/grpc_cpp_plugin stt.proto 48 | 49 | 50 | ../thirdparty/inst/include/grpc/grpc.h: 51 | make -C ../thirdparty inst/include/grpc/grpc.h 52 | 53 | ../thirdparty/inst/lib/libjansson.a: 54 | make -C ../thirdparty inst/lib/libjansson.a 55 | 56 | roots.pem.h: ../thirdparty/grpc/etc/roots.pem 57 | echo "static unsigned char grpc_roots_pem[] = {" > roots.pem.h.tmp 58 | xxd -i ../thirdparty/grpc/etc/roots.pem | tail -n +2 | head -n -1 >> roots.pem.h.tmp 59 | mv roots.pem.h.tmp roots.pem.h 60 | 61 | XMLDOC_FILES = app_grpcsttbackground.c 62 | 63 | all-local: .xmldocs/app_grpcsttbackground-en_US.xml 64 | 65 | .xmldocs/app_grpcsttbackground-en_US.xml_tmp: $(XMLDOC_FILES) 66 | mkdir -p .xmldocs 67 | @echo "Generating $@..." 68 | @echo "" > $@ 69 | @echo "" >> $@ 70 | @echo "" >> $@ 71 | @for i in $(XMLDOC_FILES); do \ 72 | $(AWK) -f awk_get_documentation $$i >> $@ ; \ 73 | done ; 74 | @echo "" >> $@ 75 | @echo "Generating $@ done" 76 | 77 | .xmldocs/app_grpcsttbackground-en_US.xml: $(XMLDOC_FILES) 78 | echo $@_tmp 79 | make $@_tmp || (rm -f $@_tmp; false) 80 | mv $@_tmp $@ 81 | 82 | install-data-local: .xmldocs/app_grpcsttbackground-en_US.xml 83 | test -d $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty || $(mkinstalldirs) $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 84 | $(INSTALL) -m 644 .xmldocs/app_grpcsttbackground-en_US.xml $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 85 | 86 | 87 | clobber: distclean 88 | rm -rf ./{configure,aclocal.m4,libtool,ltmain.sh,autom4te.cache,config.h.in,Makefile.in,google,roots.pem.h.tmp,.xmldocs} 89 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/app_grpcsttbackground/NEWS -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/README: -------------------------------------------------------------------------------- 1 | See INSTALL 2 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/awk_get_documentation: -------------------------------------------------------------------------------- 1 | /\/\*\*\* DOCUMENTATION/ {printit=1; next} 2 | /\*\*\*\// {if (printit) exit} 3 | {if (printit) print} 4 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | libtoolize 4 | aclocal 5 | autoheader 6 | automake 7 | autoconf 8 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Wrapper for compilers which do not understand '-c -o'. 3 | 4 | scriptversion=2012-10-14.11; # UTC 5 | 6 | # Copyright (C) 1999-2013 Free Software Foundation, Inc. 7 | # Written by Tom Tromey . 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # This file is maintained in Automake, please report 28 | # bugs to or send patches to 29 | # . 30 | 31 | nl=' 32 | ' 33 | 34 | # We need space, tab and new line, in precisely that order. Quoting is 35 | # there to prevent tools from complaining about whitespace usage. 36 | IFS=" "" $nl" 37 | 38 | file_conv= 39 | 40 | # func_file_conv build_file lazy 41 | # Convert a $build file to $host form and store it in $file 42 | # Currently only supports Windows hosts. If the determined conversion 43 | # type is listed in (the comma separated) LAZY, no conversion will 44 | # take place. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv/,$2, in 65 | *,$file_conv,*) 66 | ;; 67 | mingw/*) 68 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 69 | ;; 70 | cygwin/*) 71 | file=`cygpath -m "$file" || echo "$file"` 72 | ;; 73 | wine/*) 74 | file=`winepath -w "$file" || echo "$file"` 75 | ;; 76 | esac 77 | ;; 78 | esac 79 | } 80 | 81 | # func_cl_dashL linkdir 82 | # Make cl look for libraries in LINKDIR 83 | func_cl_dashL () 84 | { 85 | func_file_conv "$1" 86 | if test -z "$lib_path"; then 87 | lib_path=$file 88 | else 89 | lib_path="$lib_path;$file" 90 | fi 91 | linker_opts="$linker_opts -LIBPATH:$file" 92 | } 93 | 94 | # func_cl_dashl library 95 | # Do a library search-path lookup for cl 96 | func_cl_dashl () 97 | { 98 | lib=$1 99 | found=no 100 | save_IFS=$IFS 101 | IFS=';' 102 | for dir in $lib_path $LIB 103 | do 104 | IFS=$save_IFS 105 | if $shared && test -f "$dir/$lib.dll.lib"; then 106 | found=yes 107 | lib=$dir/$lib.dll.lib 108 | break 109 | fi 110 | if test -f "$dir/$lib.lib"; then 111 | found=yes 112 | lib=$dir/$lib.lib 113 | break 114 | fi 115 | if test -f "$dir/lib$lib.a"; then 116 | found=yes 117 | lib=$dir/lib$lib.a 118 | break 119 | fi 120 | done 121 | IFS=$save_IFS 122 | 123 | if test "$found" != yes; then 124 | lib=$lib.lib 125 | fi 126 | } 127 | 128 | # func_cl_wrapper cl arg... 129 | # Adjust compile command to suit cl 130 | func_cl_wrapper () 131 | { 132 | # Assume a capable shell 133 | lib_path= 134 | shared=: 135 | linker_opts= 136 | for arg 137 | do 138 | if test -n "$eat"; then 139 | eat= 140 | else 141 | case $1 in 142 | -o) 143 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 144 | eat=1 145 | case $2 in 146 | *.o | *.[oO][bB][jJ]) 147 | func_file_conv "$2" 148 | set x "$@" -Fo"$file" 149 | shift 150 | ;; 151 | *) 152 | func_file_conv "$2" 153 | set x "$@" -Fe"$file" 154 | shift 155 | ;; 156 | esac 157 | ;; 158 | -I) 159 | eat=1 160 | func_file_conv "$2" mingw 161 | set x "$@" -I"$file" 162 | shift 163 | ;; 164 | -I*) 165 | func_file_conv "${1#-I}" mingw 166 | set x "$@" -I"$file" 167 | shift 168 | ;; 169 | -l) 170 | eat=1 171 | func_cl_dashl "$2" 172 | set x "$@" "$lib" 173 | shift 174 | ;; 175 | -l*) 176 | func_cl_dashl "${1#-l}" 177 | set x "$@" "$lib" 178 | shift 179 | ;; 180 | -L) 181 | eat=1 182 | func_cl_dashL "$2" 183 | ;; 184 | -L*) 185 | func_cl_dashL "${1#-L}" 186 | ;; 187 | -static) 188 | shared=false 189 | ;; 190 | -Wl,*) 191 | arg=${1#-Wl,} 192 | save_ifs="$IFS"; IFS=',' 193 | for flag in $arg; do 194 | IFS="$save_ifs" 195 | linker_opts="$linker_opts $flag" 196 | done 197 | IFS="$save_ifs" 198 | ;; 199 | -Xlinker) 200 | eat=1 201 | linker_opts="$linker_opts $2" 202 | ;; 203 | -*) 204 | set x "$@" "$1" 205 | shift 206 | ;; 207 | *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) 208 | func_file_conv "$1" 209 | set x "$@" -Tp"$file" 210 | shift 211 | ;; 212 | *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) 213 | func_file_conv "$1" mingw 214 | set x "$@" "$file" 215 | shift 216 | ;; 217 | *) 218 | set x "$@" "$1" 219 | shift 220 | ;; 221 | esac 222 | fi 223 | shift 224 | done 225 | if test -n "$linker_opts"; then 226 | linker_opts="-link$linker_opts" 227 | fi 228 | exec "$@" $linker_opts 229 | exit 1 230 | } 231 | 232 | eat= 233 | 234 | case $1 in 235 | '') 236 | echo "$0: No command. Try '$0 --help' for more information." 1>&2 237 | exit 1; 238 | ;; 239 | -h | --h*) 240 | cat <<\EOF 241 | Usage: compile [--help] [--version] PROGRAM [ARGS] 242 | 243 | Wrapper for compilers which do not understand '-c -o'. 244 | Remove '-o dest.o' from ARGS, run PROGRAM with the remaining 245 | arguments, and rename the output as expected. 246 | 247 | If you are trying to build a whole package this is not the 248 | right script to run: please start by reading the file 'INSTALL'. 249 | 250 | Report bugs to . 251 | EOF 252 | exit $? 253 | ;; 254 | -v | --v*) 255 | echo "compile $scriptversion" 256 | exit $? 257 | ;; 258 | cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) 259 | func_cl_wrapper "$@" # Doesn't return... 260 | ;; 261 | esac 262 | 263 | ofile= 264 | cfile= 265 | 266 | for arg 267 | do 268 | if test -n "$eat"; then 269 | eat= 270 | else 271 | case $1 in 272 | -o) 273 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 274 | # So we strip '-o arg' only if arg is an object. 275 | eat=1 276 | case $2 in 277 | *.o | *.obj) 278 | ofile=$2 279 | ;; 280 | *) 281 | set x "$@" -o "$2" 282 | shift 283 | ;; 284 | esac 285 | ;; 286 | *.c) 287 | cfile=$1 288 | set x "$@" "$1" 289 | shift 290 | ;; 291 | *) 292 | set x "$@" "$1" 293 | shift 294 | ;; 295 | esac 296 | fi 297 | shift 298 | done 299 | 300 | if test -z "$ofile" || test -z "$cfile"; then 301 | # If no '-o' option was seen then we might have been invoked from a 302 | # pattern rule where we don't need one. That is ok -- this is a 303 | # normal compilation that the losing compiler can handle. If no 304 | # '.c' file was seen then we are probably linking. That is also 305 | # ok. 306 | exec "$@" 307 | fi 308 | 309 | # Name of file we expect compiler to create. 310 | cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` 311 | 312 | # Create the lock directory. 313 | # Note: use '[/\\:.-]' here to ensure that we don't use the same name 314 | # that we are using for the .o file. Also, base the name on the expected 315 | # object file name, since that is what matters with a parallel build. 316 | lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d 317 | while true; do 318 | if mkdir "$lockdir" >/dev/null 2>&1; then 319 | break 320 | fi 321 | sleep 1 322 | done 323 | # FIXME: race condition here if user kills between mkdir and trap. 324 | trap "rmdir '$lockdir'; exit 1" 1 2 15 325 | 326 | # Run the compile. 327 | "$@" 328 | ret=$? 329 | 330 | if test -f "$cofile"; then 331 | test "$cofile" = "$ofile" || mv "$cofile" "$ofile" 332 | elif test -f "${cofile}bj"; then 333 | test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" 334 | fi 335 | 336 | rmdir "$lockdir" 337 | exit $ret 338 | 339 | # Local Variables: 340 | # mode: shell-script 341 | # sh-indentation: 2 342 | # eval: (add-hook 'write-file-hooks 'time-stamp) 343 | # time-stamp-start: "scriptversion=" 344 | # time-stamp-format: "%:y-%02m-%02d.%02H" 345 | # time-stamp-time-zone: "UTC" 346 | # time-stamp-end: "; # UTC" 347 | # End: 348 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([app_grpcsttbackground], [0.1]) 2 | AC_CONFIG_FILES([Makefile]) 3 | 4 | AC_CONFIG_HEADERS([config.h]) 5 | AM_INIT_AUTOMAKE 6 | AC_ENABLE_SHARED 7 | AC_DISABLE_STATIC 8 | AC_PROG_CC 9 | AC_PROG_CXX 10 | AC_PROG_LIBTOOL 11 | AC_LIBTOOL_SETUP 12 | 13 | AC_PATH_PROG(XXD, xxd) 14 | if test -z "$XXD"; then 15 | AC_MSG_ERROR([Please install 'xxd' tool]) 16 | fi 17 | 18 | asteriskmoduledir="$libdir/asterisk/modules" 19 | AC_SUBST(asteriskmoduledir) 20 | 21 | AC_ARG_WITH([asterisk-xmldoc-dir], 22 | [AS_HELP_STRING([--with-asterisk-xmldoc-dir=DIR], 23 | [Asterisk XML documentation directory @<:@default=$localstatedir/lib/asterisk/documentation@:>@])], 24 | [asterisk_xmldoc_dir=$withval], 25 | [asterisk_xmldoc_dir=$localstatedir/lib/asterisk/documentation]) 26 | if test -z "$asterisk_xmldoc_dir"; then 27 | AC_MSG_ERROR([Empty directory for '--with-asterisk-xmldoc-dir=' specified]) 28 | fi 29 | AC_SUBST(asterisk_xmldoc_dir) 30 | 31 | AC_OUTPUT 32 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/grpc_stt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPC_STT_H 20 | #define GRPC_STT_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | struct ast_channel; 27 | 28 | enum grpc_stt_frame_format { 29 | GRPC_STT_FRAME_FORMAT_ALAW = 0, 30 | GRPC_STT_FRAME_FORMAT_MULAW = 1, 31 | GRPC_STT_FRAME_FORMAT_SLINEAR16 = 2, 32 | }; 33 | 34 | extern void grpc_stt_run( 35 | int terminate_event_fd, 36 | const char *target, 37 | const char *authorization_api_key, 38 | const char *authorization_secret_key, 39 | const char *authorization_issuer, 40 | const char *authorization_subject, 41 | const char *authorization_audience, 42 | struct ast_channel *chan, 43 | int ssl_grpc, 44 | const char *ca_data, 45 | const char *language_code, 46 | int max_alternatives, 47 | enum grpc_stt_frame_format frame_format, 48 | int vad_disable, 49 | double vad_min_speech_duration, 50 | double vad_max_speech_duration, 51 | double vad_silence_duration_threshold, 52 | double vad_silence_prob_threshold, 53 | double vad_aggressiveness, 54 | int interim_results_enable, 55 | double interim_results_interval, 56 | int enable_gender_identification); 57 | 58 | #ifdef __cplusplus 59 | }; 60 | #endif 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/jwt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | extern "C" struct ast_module *AST_MODULE_SELF_SYM(void); 20 | #define AST_MODULE_SELF_SYM AST_MODULE_SELF_SYM 21 | 22 | #define _GNU_SOURCE 1 23 | #include "jwt.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | extern "C" { 31 | #include 32 | #include 33 | }; 34 | 35 | 36 | /* Base64 implementation is adapted from https://www.boost.org/doc/libs/1_70_0/boost/beast/core/detail/base64.ipp */ 37 | static const char base64_rev_alpha[] = { 38 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 39 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 40 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 41 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 42 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 43 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 44 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 45 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 46 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 47 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 48 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 49 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 50 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 51 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 52 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 53 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 54 | }; 55 | static const char base64_alpha_url_safe[] = { 56 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 57 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 58 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 59 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 60 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 61 | }; 62 | 63 | static std::string base64_decode(const std::string &src, bool *ok = NULL) 64 | { 65 | std::string dest; 66 | 67 | unsigned char const *in = reinterpret_cast(src.data()); 68 | std::size_t len = src.size(); 69 | unsigned char c3[3], c4[4]; 70 | 71 | int i = 0; 72 | while (len-- && *in != '=') { 73 | char v = base64_rev_alpha[*in]; 74 | if (v == -1) { 75 | if (ok) 76 | *ok = false; 77 | return ""; 78 | } 79 | ++in; 80 | c4[i] = v; 81 | if (++i == 4) { 82 | c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); 83 | c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); 84 | c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; 85 | 86 | dest.append(reinterpret_cast(c3), 3); 87 | 88 | i = 0; 89 | } 90 | } 91 | 92 | if (i) { 93 | c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); 94 | c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); 95 | c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; 96 | 97 | dest.append(reinterpret_cast(c3), i - 1); 98 | } 99 | 100 | if (ok) 101 | *ok = true; 102 | return dest; 103 | } 104 | static std::string base64_encode_url_safe(const std::string &src) 105 | { 106 | std::string dest; 107 | 108 | const char *in = src.data(); 109 | const char *tab = base64_alpha_url_safe; 110 | 111 | size_t len = src.size(); 112 | 113 | for (size_t n = len/3; n--;) { 114 | char chunk[] = { 115 | tab[ (in[0] & 0xfc) >> 2], 116 | tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)], 117 | tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)], 118 | tab[ in[2] & 0x3f], 119 | }; 120 | dest.append(chunk, 4); 121 | in += 3; 122 | } 123 | 124 | switch (len % 3) { 125 | case 2: { 126 | char chunk[] = { 127 | tab[ (in[0] & 0xfc) >> 2], 128 | tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)], 129 | tab[ (in[1] & 0x0f) << 2], 130 | '=', 131 | }; 132 | dest.append(chunk, 4); 133 | } break; 134 | case 1: { 135 | char chunk[] = { 136 | tab[ (in[0] & 0xfc) >> 2], 137 | tab[((in[0] & 0x03) << 4)], 138 | '=', 139 | '=', 140 | }; 141 | dest.append(chunk, 4); 142 | } 143 | } 144 | 145 | return dest; 146 | } 147 | 148 | static std::string build_header(const std::string &api_key) 149 | { 150 | struct ast_json *json = ast_json_pack( 151 | "{s: s, s: s, s: s}", 152 | "alg", "HS256", 153 | "typ", "JWT", 154 | "kid", api_key.c_str()); 155 | if (!json) 156 | return ""; 157 | 158 | char *serialized = ast_json_dump_string(json); 159 | std::string ret(serialized ? serialized : ""); 160 | ast_json_free(serialized); 161 | 162 | ast_json_unref(json); 163 | 164 | return ret; 165 | } 166 | static std::string build_payload(const std::string &issuer, const std::string &subject, const std::string &audience, int64_t expires_at) 167 | { 168 | double expires_at_value = expires_at; 169 | struct ast_json *json = ast_json_pack( 170 | "{s: s, s: s, s: s, s: f}", 171 | "iss", issuer.c_str(), 172 | "sub", subject.c_str(), 173 | "aud", audience.c_str(), 174 | "exp", expires_at_value); 175 | if (!json) 176 | return ""; 177 | 178 | char *serialized = ast_json_dump_string(json); 179 | std::string ret(serialized ? serialized : ""); 180 | ast_json_free(serialized); 181 | 182 | ast_json_unref(json); 183 | 184 | return ret; 185 | } 186 | 187 | 188 | std::string GenerateJWT(const std::string &api_key, const std::string &secret_key, 189 | const std::string &issuer, const std::string &subject, const std::string &audience, 190 | int64_t expiration_time_sec) 191 | { 192 | std::string jwt; 193 | 194 | std::string header_bytes = build_header(api_key); 195 | std::string payload_bytes = build_payload(issuer, subject, audience, expiration_time_sec); 196 | 197 | std::string data = base64_encode_url_safe(header_bytes) + "." + base64_encode_url_safe(payload_bytes); 198 | 199 | std::string secret_decoded = base64_decode(secret_key); 200 | 201 | unsigned char sig[SHA256_DIGEST_LENGTH]; 202 | unsigned int sig_len; 203 | unsigned char *ret = HMAC(EVP_sha256(), secret_decoded.data(), secret_decoded.size(), (const unsigned char *) data.data(), data.size(), sig, &sig_len); 204 | if (!ret) 205 | return jwt; 206 | std::string signature = std::string(reinterpret_cast(sig), sig_len); 207 | std::string signature_b64 = base64_encode_url_safe(signature); 208 | jwt = data + "." + signature_b64; 209 | 210 | return jwt; 211 | } 212 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/jwt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef JWT_H 20 | #define JWT_H 21 | 22 | #include 23 | #include 24 | 25 | std::string GenerateJWT( 26 | const std::string &api_key, 27 | const std::string &secret_key, 28 | const std::string &issuer, 29 | const std::string &subject, 30 | const std::string &audience, 31 | int64_t expiration_time_sec); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common wrapper for a few potentially missing GNU programs. 3 | 4 | scriptversion=2012-06-26.16; # UTC 5 | 6 | # Copyright (C) 1996-2013 Free Software Foundation, Inc. 7 | # Originally written by Fran,cois Pinard , 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=http://www.perl.org/ 105 | flex_URL=http://flex.sourceforge.net/ 106 | gnu_software_URL=http://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'automa4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'write-file-hooks 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /asterisk_modules/app_grpcsttbackground/samples/grpcstt.conf.sample: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | ;Speech-To-Text host:port 4 | endpoint=domain.org:443 5 | 6 | ;Use SSL. Default: no 7 | use_ssl=true 8 | 9 | ;Use external CA file (relative to configuration directory). Default: built-in CA 10 | ca_file=grpcstt_ca.pem 11 | 12 | ;Language code 13 | language_code= 14 | 15 | ;Frame format: "alaw", "ulaw" or "slin". Default: "alaw" 16 | frame_format=alaw 17 | 18 | ;Maximum number of alternatives. Default: 1 19 | max_alternatives=3 20 | 21 | 22 | [vad] 23 | 24 | ;Disable VAD (at Speech-To-Text server). Default: no 25 | disable=false 26 | 27 | ;Minimal speech duration. Default: settings at Speech-To-Text server 28 | min_speech_duration=0.3 29 | 30 | ;Maximal speech duration. Default: settings at Speech-To-Text server 31 | max_speech_duration=15.0 32 | 33 | ;Silence duration threshold. Default: settings at Speech-To-Text server 34 | silence_duration_threshold=0.3 35 | 36 | ;Silence probobality threshold. Default: settings at Speech-To-Text server 37 | silence_prob_threshold=0.9 38 | 39 | ;Aggressiveness. Default: settings at Speech-To-Text server 40 | aggressiveness=1 ; float 41 | 42 | 43 | [interim_results] 44 | 45 | ;Enable interim recognition results. Default: no 46 | enable=true 47 | 48 | ;Interim results interval. Default: settings at Speech-To-Text server 49 | interval=3 50 | 51 | [gender_identification] 52 | 53 | ;Enable gender identification. Default: no 54 | enable=true 55 | 56 | [authorization] 57 | 58 | ;Set API key for authorization. Default: "" 59 | api_key=abcdefghijklmbhopABCDEFGhijlLMNOPQRSTUVWXYZ=TEST 60 | 61 | ;Set secret key for authorization. Default: "" 62 | secret_key=abcdefghijklmbhopABCDEFGhijlLMNOPQRSTUVWXYZ3211123 63 | 64 | ;Set issuer for authorization. Default: "" 65 | issuer=issuer 66 | 67 | ;Set subject for authorization. Default: "" 68 | subject=subject 69 | 70 | ;Set audience for authorization. Default: "" 71 | audience=tinkoff.cloud.stt 72 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/AUTHORS: -------------------------------------------------------------------------------- 1 | Grigoriy Okopnik 2 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/app_playbackground/ChangeLog -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/INSTALL: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | ``` 5 | ./configure --prefix=PREFIX 6 | make 7 | make install 8 | ``` 9 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/Makefile.am: -------------------------------------------------------------------------------- 1 | pkgdir = $(asteriskmoduledir) 2 | pkg_LTLIBRARIES = app_playbackground.la 3 | PROTO_BUILT_SOURCES = \ 4 | tts.pb.cc tts.pb.h \ 5 | tts.grpc.pb.cc tts.grpc.pb.h \ 6 | google/api/annotations.pb.cc google/api/annotations.pb.h \ 7 | google/api/http.pb.cc google/api/http.pb.h 8 | BUILT_SOURCES = ../thirdparty/inst/include/grpc/grpc.h roots.pem.h $(PROTO_BUILT_SOURCES) 9 | 10 | app_playbackground_la_SOURCES = \ 11 | app_playbackground.c \ 12 | stream_layers.c \ 13 | bytequeue.cpp \ 14 | channelbackend.cpp \ 15 | channel.cpp \ 16 | grpctts.cpp \ 17 | grpctts_conf.c \ 18 | job.cpp \ 19 | jwt.cpp \ 20 | $(PROTO_BUILT_SOURCES) 21 | 22 | app_playbackground_la_CFLAGS = -Wall -pthread -O3 -Werror=implicit-function-declaration -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -fPIC -DAST_MODULE=\"app_playbackground\" \ 23 | -DASTERISK_MODULE_VERSION_STRING=\"`git describe --tags --always`\" $(OPUS_CFLAGS) 24 | app_playbackground_la_CXXFLAGS = -Wall -pthread -O3 -std=c++11 -I../thirdparty/inst/include -fPIC $(OPUS_CFLAGS) 25 | app_playbackground_la_LDFLAGS = -Wl,-E -pthread -g -module -avoid-version -ldl -Wl,-fuse-ld=gold \ 26 | $(OPUS_LIBS) \ 27 | ../thirdparty/inst/lib/libprotobuf.a \ 28 | ../thirdparty/inst/lib/libprotoc.a \ 29 | ../thirdparty/inst/lib/libaddress_sorting.a \ 30 | ../thirdparty/inst/lib/libgpr.a \ 31 | ../thirdparty/inst/lib/libgrpc.a \ 32 | ../thirdparty/inst/lib/libgrpc++.a \ 33 | ../thirdparty/inst/lib/libgrpc_cronet.a \ 34 | ../thirdparty/inst/lib/libgrpc++_cronet.a \ 35 | ../thirdparty/inst/lib/libgrpc++_error_details.a \ 36 | ../thirdparty/inst/lib/libgrpc++_reflection.a \ 37 | ../thirdparty/inst/lib/libgrpc_unsecure.a \ 38 | ../thirdparty/inst/lib/libgrpc++_unsecure.a 39 | app_playbackground_la_LIBTOOLFLAGS = --tag=disable-static 40 | 41 | CLEANFILES=$(PROTO_BUILT_SOURCES) roots.pem.h 42 | 43 | 44 | tts.pb.cc tts.pb.h: tts.proto 45 | ../thirdparty/inst/bin/protoc -I . -I ../thirdparty/googleapis --cpp_out=. tts.proto 46 | 47 | google/api/annotations.pb.cc google/api/annotations.pb.h: ../thirdparty/googleapis/google/api/annotations.proto 48 | ../thirdparty/inst/bin/protoc -I ../thirdparty/googleapis --cpp_out=. ../thirdparty/googleapis/google/api/annotations.proto 49 | 50 | google/api/http.pb.cc google/api/http.pb.h: ../thirdparty/googleapis/google/api/http.proto 51 | ../thirdparty/inst/bin/protoc -I ../thirdparty/googleapis --cpp_out=. ../thirdparty/googleapis/google/api/http.proto 52 | 53 | tts.grpc.pb.cc tts.grpc.pb.h: tts.proto 54 | ../thirdparty/inst/bin/protoc -I . -I ../thirdparty/googleapis --grpc_out=. --plugin=protoc-gen-grpc=../thirdparty/inst/bin/grpc_cpp_plugin tts.proto 55 | 56 | 57 | ../thirdparty/inst/include/grpc/grpc.h: 58 | make -C ../thirdparty inst/include/grpc/grpc.h 59 | 60 | roots.pem.h: ../thirdparty/grpc/etc/roots.pem 61 | echo "static unsigned char grpc_roots_pem[] = {" > roots.pem.h.tmp 62 | xxd -i ../thirdparty/grpc/etc/roots.pem | tail -n +2 | head -n -1 >> roots.pem.h.tmp 63 | mv roots.pem.h.tmp roots.pem.h 64 | 65 | XMLDOC_FILES = app_playbackground.c 66 | 67 | all-local: .xmldocs/app_playbackground-en_US.xml 68 | 69 | .xmldocs/app_playbackground-en_US.xml_tmp: $(XMLDOC_FILES) 70 | mkdir -p .xmldocs 71 | @echo "Generating $@..." 72 | @echo "" > $@ 73 | @echo "" >> $@ 74 | @echo "" >> $@ 75 | @for i in $(XMLDOC_FILES); do \ 76 | $(AWK) -f awk_get_documentation $$i >> $@ ; \ 77 | done ; 78 | @echo "" >> $@ 79 | @echo "Generating $@ done" 80 | 81 | .xmldocs/app_playbackground-en_US.xml: $(XMLDOC_FILES) 82 | echo $@_tmp 83 | make $@_tmp || (rm -f $@_tmp; false) 84 | mv $@_tmp $@ 85 | 86 | install-data-local: .xmldocs/app_playbackground-en_US.xml 87 | test -d $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty || $(mkinstalldirs) $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 88 | $(INSTALL) -m 644 .xmldocs/app_playbackground-en_US.xml $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 89 | 90 | 91 | clobber: distclean 92 | rm -rf ./{configure,aclocal.m4,libtool,ltmain.sh,autom4te.cache,config.h.in,Makefile.in,google,roots.pem.h.tmp,.xmldocs} 93 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/app_playbackground/NEWS -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/RAII.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef RAII_H 20 | #define RAII_H 21 | 22 | #include 23 | #include 24 | 25 | 26 | template 27 | class RAII 28 | { 29 | public: 30 | RAII(const RAII &) = delete; 31 | RAII &operator=(const RAII &) = delete; 32 | 33 | RAII(RAII &&) noexcept = default; 34 | 35 | explicit RAII(Op &&op) 36 | : op(std::forward(op)) 37 | { 38 | } 39 | ~RAII() 40 | { 41 | op(); 42 | } 43 | 44 | private: 45 | Op op; 46 | }; 47 | 48 | template 49 | RAII BuildSafeRAII(Op &&op) 50 | { 51 | try { 52 | return RAII(std::forward(op)); 53 | } catch(...) { 54 | op(); 55 | throw std::current_exception; 56 | } 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/README: -------------------------------------------------------------------------------- 1 | See INSTALL 2 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/awk_get_documentation: -------------------------------------------------------------------------------- 1 | /\/\*\*\* DOCUMENTATION/ {printit=1; next} 2 | /\*\*\*\// {if (printit) exit} 3 | {if (printit) print} 4 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | libtoolize 4 | aclocal 5 | autoheader 6 | automake 7 | autoconf 8 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/bytequeue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #include "bytequeue.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | 28 | namespace GRPCTTS { 29 | 30 | struct Record 31 | { 32 | Record(bool completion_success) 33 | : completion_success(completion_success), next(NULL) 34 | { 35 | } 36 | Record(const std::string &data) 37 | : data(data), completion_success(false), next(NULL) 38 | { 39 | } 40 | bool IsEmpty() 41 | { 42 | return data.empty(); 43 | } 44 | 45 | std::string data; 46 | bool completion_success; 47 | Record* next; 48 | }; 49 | 50 | 51 | ByteQueue::ByteQueue() 52 | : event_fd(eventfd(0, EFD_NONBLOCK)), exchange_head(NULL), 53 | recieved_head(NULL), recieved_tail_p(&recieved_head), head_offset(0), recieved_byte_count(0), 54 | termination_pushed(false), termination_called(false), completion_success(false) 55 | { 56 | } 57 | ByteQueue::~ByteQueue() 58 | { 59 | Collect(); 60 | while (recieved_head) { 61 | Record *record = recieved_head; 62 | recieved_head = recieved_head->next; 63 | delete record; 64 | } 65 | close(event_fd); 66 | } 67 | void ByteQueue::Push(const std::string &data) 68 | { 69 | if (termination_pushed) 70 | return; 71 | PushRecord(new Record(data)); 72 | } 73 | void ByteQueue::Terminate(bool completion_success) 74 | { 75 | if (termination_pushed) 76 | return; 77 | PushRecord(new Record(completion_success)); 78 | termination_pushed = true; 79 | } 80 | int ByteQueue::EventFD() 81 | { 82 | return event_fd; 83 | } 84 | bool ByteQueue::TerminationCalled() 85 | { 86 | return termination_called; 87 | } 88 | bool ByteQueue::CompletionSuccess() 89 | { 90 | return completion_success; 91 | } 92 | bool ByteQueue::Collect() 93 | { 94 | eventfd_t value = 0; 95 | eventfd_read(event_fd, &value); 96 | Record *new_exchange_head = NULL; 97 | Record *reversed_recieved_head; 98 | __atomic_exchange(&exchange_head, &new_exchange_head, &reversed_recieved_head, __ATOMIC_SEQ_CST); 99 | if (!reversed_recieved_head) 100 | return false; 101 | StoreReverseList(reversed_recieved_head); 102 | return true; 103 | } 104 | size_t ByteQueue::BufferSize() 105 | { 106 | return recieved_byte_count; 107 | } 108 | bool ByteQueue::TakeBlock(size_t byte_count, void *data) 109 | { 110 | if (byte_count > recieved_byte_count) 111 | return false; 112 | ExtractBytes(byte_count, data); 113 | return true; 114 | } 115 | size_t ByteQueue::TakeTail(size_t byte_count, void *data) 116 | { 117 | size_t write_left = std::min(byte_count, recieved_byte_count); 118 | ExtractBytes(write_left, data); 119 | return write_left; 120 | } 121 | void ByteQueue::PushRecord(Record *object) 122 | { 123 | __atomic_exchange(&exchange_head, &object, &object->next, __ATOMIC_SEQ_CST); 124 | eventfd_write(event_fd, 1); 125 | } 126 | void ByteQueue::ExtractBytes(size_t byte_count, void *data) 127 | { 128 | size_t write_left = byte_count; 129 | char *dptr = (char *) data; 130 | while (write_left > 0) { 131 | Record *head = recieved_head; 132 | size_t left_at_record = head->data.size() - head_offset; 133 | if (write_left >= left_at_record) { 134 | memcpy(dptr, head->data.data() + head_offset, left_at_record); 135 | dptr += left_at_record; 136 | recieved_byte_count -= left_at_record; 137 | write_left -= left_at_record; 138 | recieved_head = head->next; 139 | delete head; 140 | head_offset = 0; 141 | if (!recieved_head) 142 | recieved_tail_p = &recieved_head; 143 | } else { 144 | memcpy(dptr, head->data.data() + head_offset, write_left); 145 | head_offset += write_left; 146 | recieved_byte_count -= write_left; 147 | break; 148 | } 149 | } 150 | } 151 | void ByteQueue::StoreReverseList(Record *queue) 152 | { 153 | // 1. Reverse 154 | Record **new_tail = &queue->next; 155 | Record *prev = queue; 156 | queue = queue->next; 157 | prev->next = NULL; 158 | while (queue) { 159 | Record *next = queue->next; 160 | queue->next = prev; 161 | prev = queue; 162 | queue = next; 163 | } 164 | Record *list_head = prev; 165 | 166 | // 2. Append to current list 167 | *recieved_tail_p = list_head; 168 | recieved_tail_p = new_tail; 169 | 170 | // 3. Count bytes and set termination flag and status 171 | while (list_head) { 172 | if (list_head->IsEmpty()) { 173 | termination_called = true; 174 | completion_success = list_head->completion_success; 175 | } 176 | recieved_byte_count += list_head->data.size(); 177 | list_head = list_head->next; 178 | } 179 | } 180 | 181 | }; 182 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/bytequeue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPCTTS_BYTE_QUEUE_H 20 | #define GRPCTTS_BYTE_QUEUE_H 21 | 22 | #include 23 | #include 24 | 25 | 26 | namespace GRPCTTS { 27 | 28 | struct Record; 29 | 30 | // Single sender + single reciever byte queue 31 | class ByteQueue 32 | { 33 | public: 34 | ByteQueue(); 35 | ~ByteQueue(); 36 | 37 | // Sender-only methods 38 | void Push(const std::string &data); 39 | void Terminate(bool success); 40 | 41 | // Reader-only methods 42 | int EventFD(); 43 | bool TerminationCalled(); 44 | bool CompletionSuccess(); 45 | bool Collect(); 46 | size_t BufferSize(); 47 | bool TakeBlock(size_t byte_count, void *data); 48 | size_t TakeTail(size_t byte_count, void *data); 49 | 50 | private: 51 | void PushRecord(Record *object); 52 | void ExtractBytes(size_t byte_count, void *data); 53 | void StoreReverseList(Record *queue); 54 | 55 | private: 56 | int event_fd; 57 | Record *exchange_head; 58 | Record *recieved_head; 59 | Record **recieved_tail_p; 60 | size_t head_offset; 61 | size_t recieved_byte_count; 62 | bool termination_pushed; 63 | bool termination_called; 64 | bool completion_success; 65 | }; 66 | 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/channel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #include "channel.h" 20 | 21 | #include "job.h" 22 | #include "channelbackend.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | namespace GRPCTTS { 33 | 34 | Channel::Channel(const char *endpoint, const char *ca_data, const char *authorization_api_key, const char *authorization_secret_key, 35 | const char *authorization_issuer, const char *authorization_subject, const char *authorization_audience) 36 | : channel_backend (std::make_shared (endpoint, ca_data, authorization_api_key, authorization_secret_key, 37 | authorization_issuer, authorization_subject, authorization_audience)) 38 | { 39 | } 40 | Channel::~Channel() 41 | { 42 | } 43 | Job *Channel::StartJob(double speaking_rate, double pitch, double volume_gain_db, 44 | const std::string &voice_language_code, const std::string &voice_name, enum tinkoff::cloud::tts::v1::SsmlVoiceGender ssml_gender, 45 | enum grpctts_frame_format remote_frame_format, 46 | const struct grpctts_job_input &job_input) 47 | { 48 | return new Job(channel_backend, 49 | speaking_rate, pitch, volume_gain_db, 50 | voice_language_code, voice_name, ssml_gender, remote_frame_format, 51 | job_input); 52 | } 53 | 54 | }; 55 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/channel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPCTTS_CHANNEL_H 20 | #define GRPCTTS_CHANNEL_H 21 | 22 | #include "tts.grpc.pb.h" 23 | #include "grpctts.h" 24 | 25 | #include 26 | #include 27 | 28 | 29 | struct grpctts_job_input; 30 | 31 | namespace grpc { 32 | class Channel; 33 | }; 34 | 35 | 36 | namespace GRPCTTS { 37 | 38 | class Job; 39 | class ChannelBackend; 40 | 41 | 42 | class Channel 43 | { 44 | public: 45 | Channel(const char *endpoint, const char *ca_data, const char *authorization_api_key, const char *authorization_secret_key, 46 | const char *authorization_issuer, const char *authorization_subject, const char *authorization_audience); 47 | ~Channel(); 48 | Job *StartJob(double speaking_rate, double pitch, double volume_gain_db, 49 | const std::string &voice_language_code, const std::string &voice_name, enum tinkoff::cloud::tts::v1::SsmlVoiceGender ssml_gender, 50 | enum grpctts_frame_format remote_frame_format, 51 | const struct grpctts_job_input &job_input); 52 | 53 | private: 54 | std::shared_ptr channel_backend; 55 | }; 56 | 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/channelbackend.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #include "channelbackend.h" 20 | 21 | #include "roots.pem.h" 22 | #include "job.h" 23 | #include "jwt.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | 37 | // 7 days 38 | #define EXPIRATION_PERIOD (7*86400) 39 | 40 | 41 | static grpctts_stream_error_callback_t grpctts_stream_error_callback = NULL; 42 | 43 | static const std::string grpc_roots_pem_string ((const char *) grpc_roots_pem, sizeof(grpc_roots_pem)); 44 | 45 | 46 | static int *fd_rc_copy(int *p) 47 | { 48 | __atomic_add_fetch(&p[1], 1, __ATOMIC_SEQ_CST); 49 | return p; 50 | } 51 | static void fd_rc_destroy(int *p) 52 | { 53 | if (!p) return; 54 | 55 | int count = __atomic_add_fetch(&p[1], -1, __ATOMIC_SEQ_CST); 56 | if (count <= 0) { 57 | close(p[0]); 58 | free(p); 59 | } 60 | } 61 | static int fd_rc_cmp(int *p, int *q) 62 | { 63 | if (*p < *q) 64 | return -1; 65 | if (*p > *q) 66 | return 1; 67 | return 0; 68 | } 69 | 70 | static const grpc_arg_pointer_vtable fd_rc_vtable = { 71 | .copy = (void *(*)(void *)) fd_rc_copy, 72 | .destroy = (void (*)(void *)) fd_rc_destroy, 73 | .cmp = (int (*)(void *, void *)) fd_rc_cmp, 74 | }; 75 | 76 | static int *fd_rc_new(int fd) 77 | { 78 | int *fd_rc = (int *) malloc(sizeof(int[2])); 79 | if (!fd_rc) 80 | return nullptr; 81 | fd_rc[0] = fd; 82 | fd_rc[1] = 1; 83 | return fd_rc; 84 | } 85 | 86 | static void thread_routine(int channel_completion_fd, const std::string &endpoint, const std::string &ca_data, int socket_fd_pipe_fd, GRPCTTS::ChannelBackend *channel_backend) 87 | { 88 | grpc::SslCredentialsOptions ssl_credentials_options = { 89 | .pem_root_certs = ca_data.length() ? ca_data : grpc_roots_pem_string, 90 | }; 91 | 92 | std::shared_ptr channel_credentials = grpc::SslCredentials(ssl_credentials_options); 93 | grpc::ChannelArguments arguments; 94 | if (socket_fd_pipe_fd != -1) { 95 | int *fd_rc = fd_rc_new(socket_fd_pipe_fd); 96 | if (fd_rc) { 97 | arguments.SetPointerWithVtable("socket_fd_pass_socket_fd", fd_rc, &fd_rc_vtable); 98 | fd_rc_destroy(fd_rc); 99 | } 100 | } 101 | 102 | std::shared_ptr grpc_channel = grpc::CreateCustomChannel(endpoint, channel_credentials, arguments); 103 | channel_backend->SetChannel(grpc_channel); 104 | eventfd_write(channel_completion_fd, 1); 105 | } 106 | 107 | 108 | namespace GRPCTTS { 109 | 110 | void ChannelBackend::SetErrorCallback(grpctts_stream_error_callback_t callback) 111 | { 112 | grpctts_stream_error_callback = callback; 113 | } 114 | 115 | #define NON_NULL_STRING(str) ((str) ? (str) : "") 116 | ChannelBackend::ChannelBackend(const char *endpoint, const char *ca_data, const char *authorization_api_key, const char *authorization_secret_key, 117 | const char *authorization_issuer, const char *authorization_subject, const char *authorization_audience) 118 | : socket_fd_pass_socket_fd(-1), channel_completion_fd(eventfd(0, EFD_NONBLOCK)), 119 | authorization_api_key(NON_NULL_STRING(authorization_api_key)), authorization_secret_key(NON_NULL_STRING(authorization_secret_key)), 120 | authorization_issuer(NON_NULL_STRING(authorization_issuer)), authorization_subject(NON_NULL_STRING(authorization_subject)), authorization_audience(NON_NULL_STRING(authorization_audience)) 121 | { 122 | int socket_fd_pass_write_socket_fd = -1; 123 | { 124 | int socket_pair[2]; 125 | if (!socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair)) { 126 | socket_fd_pass_socket_fd = socket_pair[0]; 127 | socket_fd_pass_write_socket_fd = socket_pair[1]; 128 | } 129 | } 130 | 131 | thread = std::thread(thread_routine, channel_completion_fd, std::string(endpoint), std::string(ca_data ? ca_data : ""), socket_fd_pass_write_socket_fd, this); 132 | } 133 | #undef NON_NULL_STRING 134 | ChannelBackend::~ChannelBackend() 135 | { 136 | if (socket_fd_pass_socket_fd != -1) { 137 | int socket_fd; 138 | if (recv(socket_fd_pass_socket_fd, &socket_fd, sizeof(int), MSG_DONTWAIT) == sizeof(int)) { 139 | shutdown(socket_fd, SHUT_RDWR); 140 | close(socket_fd); 141 | } 142 | close(socket_fd_pass_socket_fd); 143 | } 144 | thread.join(); 145 | close(channel_completion_fd); 146 | } 147 | void ChannelBackend::SetChannel(std::shared_ptr grpc_channel) 148 | { 149 | this->grpc_channel = grpc_channel; 150 | } 151 | std::shared_ptr ChannelBackend::GetChannel() 152 | { 153 | return grpc_channel; 154 | } 155 | int ChannelBackend::ChannelCompletionFD() const 156 | { 157 | return channel_completion_fd; 158 | } 159 | std::string ChannelBackend::BuildAuthToken() const 160 | { 161 | if (authorization_api_key.size() && authorization_secret_key.size() && 162 | authorization_issuer.size() && authorization_subject.size() && authorization_audience.size()) { 163 | int64_t expiration_time_sec = time(NULL) + EXPIRATION_PERIOD; 164 | 165 | std::string jwt = "Bearer " + GenerateJWT( 166 | authorization_api_key, authorization_secret_key, 167 | authorization_issuer, authorization_subject, authorization_audience, 168 | expiration_time_sec); 169 | return jwt; 170 | } 171 | return ""; 172 | } 173 | 174 | }; 175 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/channelbackend.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPCTTS_CHANNEL_BACKEND_H 20 | #define GRPCTTS_CHANNEL_BACKEND_H 21 | 22 | #include 23 | #include 24 | 25 | 26 | typedef void (*grpctts_stream_error_callback_t)(const char *message); 27 | 28 | 29 | struct grpctts_job_input; 30 | 31 | namespace grpc { 32 | class Channel; 33 | }; 34 | 35 | 36 | namespace GRPCTTS { 37 | 38 | class ChannelBackend 39 | { 40 | public: 41 | static void SetErrorCallback(grpctts_stream_error_callback_t callback); 42 | 43 | public: 44 | ChannelBackend(const char *endpoint, const char *ca_data, const char *authorization_api_key, const char *authorization_secret_key, 45 | const char *authorization_issuer, const char *authorization_subject, const char *authorization_audience); 46 | ~ChannelBackend(); 47 | void SetChannel(std::shared_ptr grpc_channel); 48 | std::shared_ptr GetChannel(); // To be called after polling on channel_completion_fd shows some data 49 | int ChannelCompletionFD() const; 50 | std::string BuildAuthToken() const; 51 | 52 | private: 53 | int socket_fd_pass_socket_fd; 54 | std::shared_ptr grpc_channel; 55 | int channel_completion_fd; 56 | const std::string authorization_api_key; 57 | const std::string authorization_secret_key; 58 | const std::string authorization_issuer; 59 | const std::string authorization_subject; 60 | const std::string authorization_audience; 61 | std::thread thread; 62 | }; 63 | 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Wrapper for compilers which do not understand '-c -o'. 3 | 4 | scriptversion=2012-10-14.11; # UTC 5 | 6 | # Copyright (C) 1999-2013 Free Software Foundation, Inc. 7 | # Written by Tom Tromey . 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # This file is maintained in Automake, please report 28 | # bugs to or send patches to 29 | # . 30 | 31 | nl=' 32 | ' 33 | 34 | # We need space, tab and new line, in precisely that order. Quoting is 35 | # there to prevent tools from complaining about whitespace usage. 36 | IFS=" "" $nl" 37 | 38 | file_conv= 39 | 40 | # func_file_conv build_file lazy 41 | # Convert a $build file to $host form and store it in $file 42 | # Currently only supports Windows hosts. If the determined conversion 43 | # type is listed in (the comma separated) LAZY, no conversion will 44 | # take place. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv/,$2, in 65 | *,$file_conv,*) 66 | ;; 67 | mingw/*) 68 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 69 | ;; 70 | cygwin/*) 71 | file=`cygpath -m "$file" || echo "$file"` 72 | ;; 73 | wine/*) 74 | file=`winepath -w "$file" || echo "$file"` 75 | ;; 76 | esac 77 | ;; 78 | esac 79 | } 80 | 81 | # func_cl_dashL linkdir 82 | # Make cl look for libraries in LINKDIR 83 | func_cl_dashL () 84 | { 85 | func_file_conv "$1" 86 | if test -z "$lib_path"; then 87 | lib_path=$file 88 | else 89 | lib_path="$lib_path;$file" 90 | fi 91 | linker_opts="$linker_opts -LIBPATH:$file" 92 | } 93 | 94 | # func_cl_dashl library 95 | # Do a library search-path lookup for cl 96 | func_cl_dashl () 97 | { 98 | lib=$1 99 | found=no 100 | save_IFS=$IFS 101 | IFS=';' 102 | for dir in $lib_path $LIB 103 | do 104 | IFS=$save_IFS 105 | if $shared && test -f "$dir/$lib.dll.lib"; then 106 | found=yes 107 | lib=$dir/$lib.dll.lib 108 | break 109 | fi 110 | if test -f "$dir/$lib.lib"; then 111 | found=yes 112 | lib=$dir/$lib.lib 113 | break 114 | fi 115 | if test -f "$dir/lib$lib.a"; then 116 | found=yes 117 | lib=$dir/lib$lib.a 118 | break 119 | fi 120 | done 121 | IFS=$save_IFS 122 | 123 | if test "$found" != yes; then 124 | lib=$lib.lib 125 | fi 126 | } 127 | 128 | # func_cl_wrapper cl arg... 129 | # Adjust compile command to suit cl 130 | func_cl_wrapper () 131 | { 132 | # Assume a capable shell 133 | lib_path= 134 | shared=: 135 | linker_opts= 136 | for arg 137 | do 138 | if test -n "$eat"; then 139 | eat= 140 | else 141 | case $1 in 142 | -o) 143 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 144 | eat=1 145 | case $2 in 146 | *.o | *.[oO][bB][jJ]) 147 | func_file_conv "$2" 148 | set x "$@" -Fo"$file" 149 | shift 150 | ;; 151 | *) 152 | func_file_conv "$2" 153 | set x "$@" -Fe"$file" 154 | shift 155 | ;; 156 | esac 157 | ;; 158 | -I) 159 | eat=1 160 | func_file_conv "$2" mingw 161 | set x "$@" -I"$file" 162 | shift 163 | ;; 164 | -I*) 165 | func_file_conv "${1#-I}" mingw 166 | set x "$@" -I"$file" 167 | shift 168 | ;; 169 | -l) 170 | eat=1 171 | func_cl_dashl "$2" 172 | set x "$@" "$lib" 173 | shift 174 | ;; 175 | -l*) 176 | func_cl_dashl "${1#-l}" 177 | set x "$@" "$lib" 178 | shift 179 | ;; 180 | -L) 181 | eat=1 182 | func_cl_dashL "$2" 183 | ;; 184 | -L*) 185 | func_cl_dashL "${1#-L}" 186 | ;; 187 | -static) 188 | shared=false 189 | ;; 190 | -Wl,*) 191 | arg=${1#-Wl,} 192 | save_ifs="$IFS"; IFS=',' 193 | for flag in $arg; do 194 | IFS="$save_ifs" 195 | linker_opts="$linker_opts $flag" 196 | done 197 | IFS="$save_ifs" 198 | ;; 199 | -Xlinker) 200 | eat=1 201 | linker_opts="$linker_opts $2" 202 | ;; 203 | -*) 204 | set x "$@" "$1" 205 | shift 206 | ;; 207 | *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) 208 | func_file_conv "$1" 209 | set x "$@" -Tp"$file" 210 | shift 211 | ;; 212 | *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) 213 | func_file_conv "$1" mingw 214 | set x "$@" "$file" 215 | shift 216 | ;; 217 | *) 218 | set x "$@" "$1" 219 | shift 220 | ;; 221 | esac 222 | fi 223 | shift 224 | done 225 | if test -n "$linker_opts"; then 226 | linker_opts="-link$linker_opts" 227 | fi 228 | exec "$@" $linker_opts 229 | exit 1 230 | } 231 | 232 | eat= 233 | 234 | case $1 in 235 | '') 236 | echo "$0: No command. Try '$0 --help' for more information." 1>&2 237 | exit 1; 238 | ;; 239 | -h | --h*) 240 | cat <<\EOF 241 | Usage: compile [--help] [--version] PROGRAM [ARGS] 242 | 243 | Wrapper for compilers which do not understand '-c -o'. 244 | Remove '-o dest.o' from ARGS, run PROGRAM with the remaining 245 | arguments, and rename the output as expected. 246 | 247 | If you are trying to build a whole package this is not the 248 | right script to run: please start by reading the file 'INSTALL'. 249 | 250 | Report bugs to . 251 | EOF 252 | exit $? 253 | ;; 254 | -v | --v*) 255 | echo "compile $scriptversion" 256 | exit $? 257 | ;; 258 | cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) 259 | func_cl_wrapper "$@" # Doesn't return... 260 | ;; 261 | esac 262 | 263 | ofile= 264 | cfile= 265 | 266 | for arg 267 | do 268 | if test -n "$eat"; then 269 | eat= 270 | else 271 | case $1 in 272 | -o) 273 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 274 | # So we strip '-o arg' only if arg is an object. 275 | eat=1 276 | case $2 in 277 | *.o | *.obj) 278 | ofile=$2 279 | ;; 280 | *) 281 | set x "$@" -o "$2" 282 | shift 283 | ;; 284 | esac 285 | ;; 286 | *.c) 287 | cfile=$1 288 | set x "$@" "$1" 289 | shift 290 | ;; 291 | *) 292 | set x "$@" "$1" 293 | shift 294 | ;; 295 | esac 296 | fi 297 | shift 298 | done 299 | 300 | if test -z "$ofile" || test -z "$cfile"; then 301 | # If no '-o' option was seen then we might have been invoked from a 302 | # pattern rule where we don't need one. That is ok -- this is a 303 | # normal compilation that the losing compiler can handle. If no 304 | # '.c' file was seen then we are probably linking. That is also 305 | # ok. 306 | exec "$@" 307 | fi 308 | 309 | # Name of file we expect compiler to create. 310 | cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` 311 | 312 | # Create the lock directory. 313 | # Note: use '[/\\:.-]' here to ensure that we don't use the same name 314 | # that we are using for the .o file. Also, base the name on the expected 315 | # object file name, since that is what matters with a parallel build. 316 | lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d 317 | while true; do 318 | if mkdir "$lockdir" >/dev/null 2>&1; then 319 | break 320 | fi 321 | sleep 1 322 | done 323 | # FIXME: race condition here if user kills between mkdir and trap. 324 | trap "rmdir '$lockdir'; exit 1" 1 2 15 325 | 326 | # Run the compile. 327 | "$@" 328 | ret=$? 329 | 330 | if test -f "$cofile"; then 331 | test "$cofile" = "$ofile" || mv "$cofile" "$ofile" 332 | elif test -f "${cofile}bj"; then 333 | test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" 334 | fi 335 | 336 | rmdir "$lockdir" 337 | exit $ret 338 | 339 | # Local Variables: 340 | # mode: shell-script 341 | # sh-indentation: 2 342 | # eval: (add-hook 'write-file-hooks 'time-stamp) 343 | # time-stamp-start: "scriptversion=" 344 | # time-stamp-format: "%:y-%02m-%02d.%02H" 345 | # time-stamp-time-zone: "UTC" 346 | # time-stamp-end: "; # UTC" 347 | # End: 348 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([app_playbackground], [0.1]) 2 | AC_CONFIG_FILES([Makefile]) 3 | 4 | AC_CONFIG_HEADERS([config.h]) 5 | AM_INIT_AUTOMAKE 6 | AC_ENABLE_SHARED 7 | AC_DISABLE_STATIC 8 | AC_PROG_CC 9 | AC_PROG_CXX 10 | AC_PROG_LIBTOOL 11 | AC_LIBTOOL_SETUP 12 | 13 | AC_PATH_PROG(XXD, xxd) 14 | if test -z "$XXD"; then 15 | AC_MSG_ERROR([Please install 'xxd' tool]) 16 | fi 17 | 18 | asteriskmoduledir="$libdir/asterisk/modules" 19 | AC_SUBST(asteriskmoduledir) 20 | 21 | AC_ARG_WITH([asterisk-xmldoc-dir], 22 | [AS_HELP_STRING([--with-asterisk-xmldoc-dir=DIR], 23 | [Asterisk XML documentation directory @<:@default=$localstatedir/lib/asterisk/documentation@:>@])], 24 | [asterisk_xmldoc_dir=$withval], 25 | [asterisk_xmldoc_dir=$localstatedir/lib/asterisk/documentation]) 26 | if test -z "$asterisk_xmldoc_dir"; then 27 | AC_MSG_ERROR([Empty directory for '--with-asterisk-xmldoc-dir=' specified]) 28 | fi 29 | AC_SUBST(asterisk_xmldoc_dir) 30 | 31 | PKG_CHECK_MODULES([OPUS],[opus >= 1.0.0]) 32 | 33 | AC_OUTPUT 34 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/grpctts.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | extern "C" struct ast_module *AST_MODULE_SELF_SYM(void); 20 | #define AST_MODULE_SELF_SYM AST_MODULE_SELF_SYM 21 | 22 | #define typeof __typeof__ 23 | #include "grpctts.h" 24 | 25 | #include "channel.h" 26 | #include "grpctts_conf.h" 27 | #include "channelbackend.h" 28 | #include "job.h" 29 | #include "tts.grpc.pb.h" 30 | 31 | #include 32 | #include 33 | 34 | 35 | extern "C" void grpctts_set_stream_error_callback(grpctts_stream_error_callback_t callback) 36 | { 37 | GRPCTTS::ChannelBackend::SetErrorCallback(callback); 38 | GRPCTTS::Job::SetErrorCallback(callback); 39 | } 40 | extern "C" void grpctts_init() 41 | { 42 | grpc_init(); 43 | } 44 | extern "C" void grpctts_shutdown() 45 | { 46 | grpc_shutdown(); 47 | } 48 | 49 | 50 | extern "C" struct grpctts_channel *grpctts_channel_create(const char *endpoint, const char *ca_data, 51 | const char *authorization_api_key, const char *authorization_secret_key, 52 | const char *authorization_issuer, const char *authorization_subject, const char *authorization_audience) 53 | { 54 | GRPCTTS::Channel *channel = new GRPCTTS::Channel(endpoint, ca_data, authorization_api_key, authorization_secret_key, 55 | authorization_issuer, authorization_subject, authorization_audience); 56 | return (struct grpctts_channel *) channel; 57 | } 58 | extern "C" void grpctts_channel_destroy(struct grpctts_channel *channel) 59 | { 60 | delete (GRPCTTS::Channel *) channel; 61 | } 62 | extern "C" struct grpctts_job *grpctts_channel_start_job(struct grpctts_channel *channel, const struct grpctts_job_conf *job_conf, const struct grpctts_job_input *job_input) 63 | { 64 | enum tinkoff::cloud::tts::v1::SsmlVoiceGender ssml_gender = tinkoff::cloud::tts::v1::SSML_VOICE_GENDER_UNSPECIFIED; 65 | switch (job_conf->voice_gender) { 66 | case GRPCTTS_VOICE_GENDER_UNSPECIFIED: 67 | ssml_gender = tinkoff::cloud::tts::v1::SSML_VOICE_GENDER_UNSPECIFIED; 68 | break; 69 | case GRPCTTS_VOICE_GENDER_MALE: 70 | ssml_gender = tinkoff::cloud::tts::v1::MALE; 71 | break; 72 | case GRPCTTS_VOICE_GENDER_FEMALE: 73 | ssml_gender = tinkoff::cloud::tts::v1::FEMALE; 74 | break; 75 | case GRPCTTS_VOICE_GENDER_NEUTRAL: 76 | ssml_gender = tinkoff::cloud::tts::v1::NEUTRAL; 77 | } 78 | return (struct grpctts_job *) ((GRPCTTS::Channel *) channel)->StartJob( 79 | job_conf->speaking_rate, job_conf->pitch, job_conf->volume_gain_db, 80 | "", (job_conf->voice_name ? job_conf->voice_name : ""), ssml_gender, 81 | job_conf->remote_frame_format, 82 | *job_input); 83 | } 84 | 85 | 86 | extern "C" void grpctts_job_destroy(struct grpctts_job *job) 87 | { 88 | delete (GRPCTTS::Job *) job; 89 | } 90 | extern "C" int grpctts_job_event_fd(struct grpctts_job *job) 91 | { 92 | return ((GRPCTTS::Job *) job)->EventFD(); 93 | } 94 | extern "C" int grpctts_job_collect(struct grpctts_job *job) 95 | { 96 | return ((GRPCTTS::Job *) job)->Collect(); 97 | } 98 | extern "C" size_t grpctts_job_buffer_size(struct grpctts_job *job) 99 | { 100 | return ((GRPCTTS::Job *) job)->BufferSize(); 101 | } 102 | extern "C" int grpctts_job_take_block(struct grpctts_job *job, size_t byte_count, void *data) 103 | { 104 | return ((GRPCTTS::Job *) job)->TakeBlock(byte_count, data); 105 | } 106 | extern "C" size_t grpctts_job_take_tail(struct grpctts_job *job, size_t byte_count, void *data) 107 | { 108 | return ((GRPCTTS::Job *) job)->TakeTail(byte_count, data); 109 | } 110 | extern "C" int grpctts_job_termination_called(struct grpctts_job *job) 111 | { 112 | return ((GRPCTTS::Job *) job)->TerminationCalled(); 113 | } 114 | extern "C" int grpctts_job_completion_success(struct grpctts_job *job) 115 | { 116 | return ((GRPCTTS::Job *) job)->CompletionSuccess(); 117 | } 118 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/grpctts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPCTTS_H 20 | #define GRPCTTS_H 21 | 22 | #include 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | struct grpctts_channel; 30 | struct grpctts_job; 31 | struct grpctts_job_conf; 32 | 33 | typedef void (*grpctts_stream_error_callback_t)(const char *message); 34 | 35 | enum grpctts_frame_format { 36 | GRPCTTS_FRAME_FORMAT_ALAW = 0, 37 | GRPCTTS_FRAME_FORMAT_MULAW = 1, 38 | GRPCTTS_FRAME_FORMAT_SLINEAR16 = 2, 39 | GRPCTTS_FRAME_FORMAT_OPUS = 3, 40 | }; 41 | 42 | struct grpctts_job_input { 43 | const char *text; 44 | const char *ssml; 45 | }; 46 | 47 | extern void grpctts_set_stream_error_callback( 48 | grpctts_stream_error_callback_t callback); 49 | 50 | extern void grpctts_init(void); 51 | 52 | extern void grpctts_shutdown(void); 53 | 54 | extern struct grpctts_channel *grpctts_channel_create( 55 | const char *endpoint, 56 | const char *ca_data, 57 | const char *authorization_api_key, const char *authorization_secret_key, 58 | const char *authorization_issuer, const char *authorization_subject, const char *authorization_audience); 59 | 60 | extern void grpctts_channel_destroy( 61 | struct grpctts_channel *channel); 62 | 63 | extern struct grpctts_job *grpctts_channel_start_job( 64 | struct grpctts_channel *channel, 65 | const struct grpctts_job_conf *job_conf, 66 | const struct grpctts_job_input *job_input); 67 | 68 | extern void grpctts_job_destroy( 69 | struct grpctts_job *job); 70 | 71 | extern int grpctts_job_event_fd( 72 | struct grpctts_job *job); 73 | 74 | extern int grpctts_job_collect( 75 | struct grpctts_job *job); 76 | 77 | extern size_t grpctts_job_buffer_size( 78 | struct grpctts_job *job); 79 | 80 | extern int grpctts_job_take_block( 81 | struct grpctts_job *job, 82 | size_t byte_count, 83 | void *data); 84 | 85 | extern size_t grpctts_job_take_tail( 86 | struct grpctts_job *job, 87 | size_t byte_count, 88 | void *data); 89 | 90 | extern int grpctts_job_termination_called( 91 | struct grpctts_job *job); 92 | 93 | extern int grpctts_job_completion_success( 94 | struct grpctts_job *job); 95 | 96 | #ifdef __cplusplus 97 | }; 98 | #endif 99 | #endif 100 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/grpctts_conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPCTTS_CONF_H 20 | #define GRPCTTS_CONF_H 21 | 22 | #define typeof __typeof__ 23 | #include 24 | #include 25 | 26 | #include "grpctts.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | 36 | enum grpctts_voice_gender { 37 | GRPCTTS_VOICE_GENDER_UNSPECIFIED = 0, 38 | GRPCTTS_VOICE_GENDER_MALE = 1, 39 | GRPCTTS_VOICE_GENDER_FEMALE = 2, 40 | GRPCTTS_VOICE_GENDER_NEUTRAL = 3, 41 | }; 42 | 43 | enum grpctts_starvation_policy { 44 | GRPCTTS_STARVATION_POLICY_UNSPECIFIED = 0, 45 | GRPCTTS_STARVATION_POLICY_WAIT = 1, /* Default */ 46 | GRPCTTS_STARVATION_POLICY_DROPOUT = 2, 47 | GRPCTTS_STARVATION_POLICY_ABANDON = 3, 48 | }; 49 | 50 | struct grpctts_buffer_size { 51 | double fraction; 52 | double seconds; 53 | }; 54 | 55 | struct grpctts_job_conf { 56 | double speaking_rate; 57 | double pitch; 58 | double volume_gain_db; 59 | char *voice_language_code; 60 | char *voice_name; 61 | enum grpctts_voice_gender voice_gender; 62 | enum grpctts_frame_format remote_frame_format; 63 | enum grpctts_starvation_policy starvation_policy; 64 | struct grpctts_buffer_size initial_buffer_size; 65 | }; 66 | 67 | struct grpctts_conf { 68 | char *endpoint; 69 | int ssl_grpc; 70 | char *ca_data; 71 | char *authorization_api_key; 72 | char *authorization_secret_key; 73 | char *authorization_issuer; 74 | char *authorization_subject; 75 | char *authorization_audience; 76 | 77 | struct grpctts_job_conf job_conf; 78 | }; 79 | 80 | #define GRPCTTS_JOB_CONF_INITIALIZER { \ 81 | .speaking_rate = 1.0, \ 82 | .pitch = 0.0, \ 83 | .volume_gain_db = 0.0, \ 84 | .voice_language_code = NULL, \ 85 | .voice_name = NULL, \ 86 | .voice_gender = GRPCTTS_VOICE_GENDER_UNSPECIFIED, \ 87 | .remote_frame_format = GRPCTTS_FRAME_FORMAT_SLINEAR16, \ 88 | } 89 | 90 | #define GRPCTTS_CONF_INITIALIZER { \ 91 | .endpoint = NULL, \ 92 | .ssl_grpc = 0, \ 93 | .ca_data = NULL, \ 94 | .authorization_api_key = NULL, \ 95 | .authorization_secret_key = NULL, \ 96 | .authorization_issuer = NULL, \ 97 | .authorization_subject = NULL, \ 98 | .authorization_audience = NULL, \ 99 | \ 100 | .job_conf = GRPCTTS_JOB_CONF_INITIALIZER, \ 101 | } 102 | 103 | 104 | extern void grpctts_conf_global_init(void); 105 | 106 | extern void grpctts_conf_global_uninit(void); 107 | 108 | 109 | extern char *grpctts_load_ca_from_file( 110 | const char *relative_fname); 111 | 112 | extern int grpctts_parse_buffer_size( 113 | struct grpctts_buffer_size *buffer_size, 114 | const char *str); 115 | 116 | extern int grpctts_parse_starvation_policy( 117 | const char *str); 118 | 119 | extern void grpctts_job_conf_init( 120 | struct grpctts_job_conf *conf); 121 | 122 | extern void grpctts_job_conf_clear( 123 | struct grpctts_job_conf *conf); 124 | 125 | extern struct grpctts_job_conf *grpctts_job_conf_cpy( 126 | struct grpctts_job_conf *dest, 127 | const struct grpctts_job_conf *src); 128 | 129 | 130 | extern void grpctts_conf_init( 131 | struct grpctts_conf *conf); 132 | 133 | extern void grpctts_conf_clear( 134 | struct grpctts_conf *conf); 135 | 136 | extern int grpctts_conf_load( 137 | struct grpctts_conf *conf, 138 | ast_mutex_t *mutex, 139 | const char *fname, 140 | int reload); 141 | 142 | extern struct grpctts_conf *grpctts_conf_cpy( 143 | struct grpctts_conf *dest, 144 | const struct grpctts_conf *src, 145 | ast_mutex_t *src_mutex); 146 | 147 | #ifdef __cplusplus 148 | }; 149 | #endif 150 | #endif 151 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/job.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #include "job.h" 20 | 21 | #include "bytequeue.h" 22 | #include "channelbackend.h" 23 | #include "grpctts.h" 24 | #include "RAII.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | #define CHANNEL_FRAME_SAMPLE_RATE 8000 43 | #define CHANNEL_MAX_OPUS_FRAME_SAMPLES 960 44 | #define CHANNEL_AWAIT_TIMEOUT 60000 /* 60 sec */ 45 | 46 | #define CXX_STRING(str) (std::string((str) ? (str) : "")) 47 | 48 | static grpctts_stream_error_callback_t grpctts_stream_error_callback = NULL; 49 | 50 | 51 | namespace GRPCTTS { 52 | 53 | static void thread_routine(std::shared_ptr channel_backend, 54 | double speaking_rate, double pitch, double volume_gain_db, 55 | const std::string &voice_language_code, const std::string &voice_name, enum tinkoff::cloud::tts::v1::SsmlVoiceGender ssml_gender, 56 | enum grpctts_frame_format remote_frame_format, 57 | const std::string &text, const std::string &ssml, std::shared_ptr byte_queue) 58 | { 59 | auto gprc_shutdown_caller = BuildSafeRAII(grpc_shutdown /* To survie module unloading */); 60 | auto byte_queue_finalizer = BuildSafeRAII([byte_queue]() 61 | { 62 | byte_queue->Terminate(false); 63 | }); 64 | OpusDecoder *opus_decoder = NULL; 65 | auto opus_finalizer = BuildSafeRAII([&opus_decoder]() 66 | { 67 | if (opus_decoder) 68 | opus_decoder_destroy(opus_decoder); 69 | }); 70 | 71 | int channel_completion_fd = channel_backend->ChannelCompletionFD(); 72 | struct pollfd pfd = { 73 | .fd = channel_completion_fd, 74 | .events = POLLIN, 75 | .revents = 0, 76 | }; 77 | poll(&pfd, 1, CHANNEL_AWAIT_TIMEOUT); 78 | if (!(pfd.revents & POLLIN)) { 79 | if (grpctts_stream_error_callback) 80 | grpctts_stream_error_callback("GRPC TTS stream finished with error: failed to initialize channel"); 81 | return; 82 | } 83 | 84 | std::shared_ptr grpc_channel = channel_backend->GetChannel(); 85 | if (!grpc_channel) { 86 | if (grpctts_stream_error_callback) 87 | grpctts_stream_error_callback("GRPC TTS stream finished with error: failed to initialize channel"); 88 | return; 89 | } 90 | 91 | grpc::ClientContext context; 92 | std::string auth_token(channel_backend->BuildAuthToken()); 93 | if (auth_token.length()) 94 | context.AddMetadata("authorization", auth_token); 95 | std::unique_ptr tts_stub = tinkoff::cloud::tts::v1::TextToSpeech::NewStub(grpc_channel); 96 | tinkoff::cloud::tts::v1::SynthesizeSpeechRequest request; 97 | 98 | if (text.size() || ssml.size()) { 99 | tinkoff::cloud::tts::v1::SynthesisInput *input = request.mutable_input(); 100 | if (text.size()) { 101 | input->set_text(text); 102 | } 103 | if (ssml.size()) { 104 | input->set_ssml(ssml); 105 | } 106 | } 107 | { 108 | tinkoff::cloud::tts::v1::VoiceSelectionParams *voice = request.mutable_voice(); 109 | voice->set_language_code(voice_language_code); 110 | voice->set_name(voice_name); 111 | voice->set_ssml_gender(ssml_gender); 112 | } 113 | { 114 | tinkoff::cloud::tts::v1::AudioConfig *audio_config = request.mutable_audio_config(); 115 | switch (remote_frame_format) { 116 | case GRPCTTS_FRAME_FORMAT_OPUS: 117 | audio_config->set_audio_encoding(tinkoff::cloud::tts::v1::RAW_OPUS); 118 | break; 119 | default: 120 | audio_config->set_audio_encoding(tinkoff::cloud::tts::v1::LINEAR16); 121 | } 122 | if (remote_frame_format == GRPCTTS_FRAME_FORMAT_OPUS) { 123 | int error; 124 | opus_decoder = opus_decoder_create(CHANNEL_FRAME_SAMPLE_RATE, 1, &error); 125 | if (error != OPUS_OK || !opus_decoder) { 126 | if (grpctts_stream_error_callback) 127 | grpctts_stream_error_callback("GRPC TTS stream finished with error: failed to initialize Opus decoder"); 128 | return; 129 | } 130 | } 131 | audio_config->set_speaking_rate(speaking_rate); 132 | // audio_config->set_pitch(pitch); - ingore for now 133 | // audio_config->set_volume_gain_db(volume_gain_db); - ingore for now 134 | audio_config->set_sample_rate_hertz(CHANNEL_FRAME_SAMPLE_RATE); 135 | } 136 | std::unique_ptr> stream = tts_stub->StreamingSynthesize(&context, request); 137 | stream->WaitForInitialMetadata(); 138 | std::string x_request_id; 139 | int64_t num_samples = -1; 140 | const std::multimap &metadata = context.GetServerInitialMetadata(); 141 | { 142 | std::multimap::const_iterator x_request_id_it = metadata.find("x-request-id"); 143 | if (x_request_id_it != metadata.end()) { 144 | const grpc::string_ref &x_request_id_ref = x_request_id_it->second; 145 | x_request_id = std::string(x_request_id_ref.data(), x_request_id_ref.size()); 146 | if (x_request_id.size() > 255) 147 | x_request_id.resize(255); 148 | } 149 | } 150 | { 151 | std::multimap::const_iterator x_audio_num_samples_it = metadata.find("x-audio-num-samples"); 152 | if (x_audio_num_samples_it != metadata.end()) { 153 | const grpc::string_ref &x_audio_num_samples_ref = x_audio_num_samples_it->second; 154 | std::string x_audio_num_samples(x_audio_num_samples_ref.data(), x_audio_num_samples_ref.size()); 155 | std::size_t conv_count; 156 | num_samples = std::stoll(x_audio_num_samples, &conv_count); 157 | if (conv_count != x_audio_num_samples.size()) 158 | num_samples = -1; 159 | } 160 | } 161 | { 162 | std::string initial_data((const char *) &num_samples, sizeof(int64_t)); 163 | initial_data.append(1, uint8_t(x_request_id.size())); 164 | initial_data.append(x_request_id); 165 | byte_queue->Push(initial_data); 166 | } 167 | 168 | tinkoff::cloud::tts::v1::StreamingSynthesizeSpeechResponse response; 169 | while (stream->Read(&response)) { 170 | switch (remote_frame_format) { 171 | case GRPCTTS_FRAME_FORMAT_OPUS: { 172 | const std::string &audio_chunk = response.audio_chunk(); 173 | int16_t frame_samples[CHANNEL_MAX_OPUS_FRAME_SAMPLES]; 174 | int num_samples_per_channel = opus_decode(opus_decoder, (const unsigned char *) audio_chunk.data(), audio_chunk.size(), 175 | frame_samples, sizeof(frame_samples)/sizeof(frame_samples[0]), 0); 176 | if (!num_samples_per_channel) { 177 | if (grpctts_stream_error_callback) 178 | grpctts_stream_error_callback("GRPC TTS stream finished with error: no audio decoded from Opus"); 179 | return; 180 | } else if (num_samples_per_channel < 0) { 181 | if (grpctts_stream_error_callback) { 182 | char message[4096]; 183 | snprintf(message, sizeof(message), "GRPC TTS stream finished with error: failed to decoded audio from Opus: %s", opus_strerror(num_samples_per_channel)); 184 | grpctts_stream_error_callback(message); 185 | } 186 | return; 187 | } 188 | byte_queue->Push(std::string((const char *) frame_samples, num_samples_per_channel*sizeof(int16_t))); 189 | } break; 190 | default: { 191 | byte_queue->Push(response.audio_chunk()); 192 | } 193 | } 194 | } 195 | grpc::Status status = stream->Finish(); 196 | byte_queue->Terminate(status.ok()); 197 | if (!status.ok() && grpctts_stream_error_callback) { 198 | char message[4096]; 199 | snprintf(message, sizeof(message), "GRPC TTS stream finished with error (code = %d): %s", (int) status.error_code(), status.error_message().c_str()); 200 | grpctts_stream_error_callback(message); 201 | } 202 | } 203 | 204 | 205 | void Job::SetErrorCallback(grpctts_stream_error_callback_t callback) 206 | { 207 | grpctts_stream_error_callback = callback; 208 | } 209 | 210 | 211 | static std::atomic Job_alloc_balance; 212 | 213 | Job::Job(std::shared_ptr channel_backend, 214 | double speaking_rate, double pitch, double volume_gain_db, 215 | const std::string &voice_language_code, const std::string &voice_name, enum tinkoff::cloud::tts::v1::SsmlVoiceGender ssml_gender, 216 | enum grpctts_frame_format remote_frame_format, const struct grpctts_job_input &job_input) 217 | : byte_queue(std::make_shared()) 218 | { 219 | grpc_init(); // To survie module unloading 220 | std::thread thread = std::thread(thread_routine, 221 | channel_backend, 222 | speaking_rate, pitch, volume_gain_db, 223 | voice_language_code, voice_name, ssml_gender, remote_frame_format, 224 | CXX_STRING(job_input.text), CXX_STRING(job_input.ssml), byte_queue); 225 | thread.detach(); 226 | } 227 | Job::~Job() 228 | { 229 | } 230 | int Job::EventFD() 231 | { 232 | return byte_queue->EventFD(); 233 | } 234 | bool Job::Collect() 235 | { 236 | return byte_queue->Collect(); 237 | } 238 | size_t Job::BufferSize() 239 | { 240 | return byte_queue->BufferSize(); 241 | } 242 | bool Job::TakeBlock(size_t byte_count, void *data) 243 | { 244 | return byte_queue->TakeBlock(byte_count, data); 245 | } 246 | size_t Job::TakeTail(size_t byte_count, void *data) 247 | { 248 | return byte_queue->TakeTail(byte_count, data); 249 | } 250 | bool Job::TerminationCalled() 251 | { 252 | return byte_queue->TerminationCalled(); 253 | } 254 | bool Job::CompletionSuccess() 255 | { 256 | return byte_queue->CompletionSuccess(); 257 | } 258 | 259 | }; 260 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/job.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef GRPCTTS_JOB_H 20 | #define GRPCTTS_JOB_H 21 | 22 | #include "tts.grpc.pb.h" 23 | #include "grpctts.h" 24 | 25 | #include 26 | #include 27 | 28 | 29 | typedef void (*grpctts_stream_error_callback_t)(const char *message); 30 | 31 | struct grpctts_job_input; 32 | 33 | namespace grpc { 34 | class Channel; 35 | }; 36 | 37 | 38 | namespace GRPCTTS { 39 | 40 | class ByteQueue; 41 | class ChannelBackend; 42 | 43 | 44 | class Job 45 | { 46 | public: 47 | static void SetErrorCallback(grpctts_stream_error_callback_t callback); 48 | 49 | public: 50 | Job(std::shared_ptr channel_backend, 51 | double speaking_rate, double pitch, double volume_gain_db, 52 | const std::string &voice_language_code, const std::string &voice_name, enum tinkoff::cloud::tts::v1::SsmlVoiceGender ssml_gender, 53 | enum grpctts_frame_format remote_frame_format, 54 | const struct grpctts_job_input &job_input); 55 | ~Job(); 56 | int EventFD(); 57 | bool Collect(); 58 | size_t BufferSize(); 59 | bool TakeBlock(size_t byte_count, void *data); 60 | size_t TakeTail(size_t byte_count, void *data); 61 | bool TerminationCalled(); 62 | bool CompletionSuccess(); 63 | 64 | private: 65 | std::shared_ptr byte_queue; 66 | }; 67 | 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/jwt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | extern "C" struct ast_module *AST_MODULE_SELF_SYM(void); 20 | #define AST_MODULE_SELF_SYM AST_MODULE_SELF_SYM 21 | 22 | #define _GNU_SOURCE 1 23 | #include "jwt.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | extern "C" { 30 | #include 31 | #include 32 | }; 33 | 34 | 35 | /* Base64 implementation is adapted from https://www.boost.org/doc/libs/1_70_0/boost/beast/core/detail/base64.ipp */ 36 | static const char base64_rev_alpha[] = { 37 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 38 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 39 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 40 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 41 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 42 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 43 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 44 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 45 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 46 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 47 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 48 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 49 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 50 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 51 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 52 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 53 | }; 54 | static const char base64_alpha_url_safe[] = { 55 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 56 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 57 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 58 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 59 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 60 | }; 61 | 62 | static std::string base64_decode(const std::string &src, bool *ok = NULL) 63 | { 64 | std::string dest; 65 | 66 | unsigned char const *in = reinterpret_cast(src.data()); 67 | std::size_t len = src.size(); 68 | unsigned char c3[3], c4[4]; 69 | 70 | int i = 0; 71 | while (len-- && *in != '=') { 72 | char v = base64_rev_alpha[*in]; 73 | if (v == -1) { 74 | if (ok) 75 | *ok = false; 76 | return ""; 77 | } 78 | ++in; 79 | c4[i] = v; 80 | if (++i == 4) { 81 | c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); 82 | c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); 83 | c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; 84 | 85 | dest.append(reinterpret_cast(c3), 3); 86 | 87 | i = 0; 88 | } 89 | } 90 | 91 | if (i) { 92 | c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); 93 | c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); 94 | c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; 95 | 96 | dest.append(reinterpret_cast(c3), i - 1); 97 | } 98 | 99 | if (ok) 100 | *ok = true; 101 | return dest; 102 | } 103 | static std::string base64_encode_url_safe(const std::string &src) 104 | { 105 | std::string dest; 106 | 107 | const char *in = src.data(); 108 | const char *tab = base64_alpha_url_safe; 109 | 110 | size_t len = src.size(); 111 | 112 | for (size_t n = len/3; n--;) { 113 | char chunk[] = { 114 | tab[ (in[0] & 0xfc) >> 2], 115 | tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)], 116 | tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)], 117 | tab[ in[2] & 0x3f], 118 | }; 119 | dest.append(chunk, 4); 120 | in += 3; 121 | } 122 | 123 | switch (len % 3) { 124 | case 2: { 125 | char chunk[] = { 126 | tab[ (in[0] & 0xfc) >> 2], 127 | tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)], 128 | tab[ (in[1] & 0x0f) << 2], 129 | '=', 130 | }; 131 | dest.append(chunk, 4); 132 | } break; 133 | case 1: { 134 | char chunk[] = { 135 | tab[ (in[0] & 0xfc) >> 2], 136 | tab[((in[0] & 0x03) << 4)], 137 | '=', 138 | '=', 139 | }; 140 | dest.append(chunk, 4); 141 | } 142 | } 143 | 144 | return dest; 145 | } 146 | 147 | static std::string build_header(const std::string &api_key) 148 | { 149 | struct ast_json *json = ast_json_pack( 150 | "{s: s, s: s, s: s}", 151 | "alg", "HS256", 152 | "typ", "JWT", 153 | "kid", api_key.c_str()); 154 | if (!json) 155 | return ""; 156 | 157 | char *serialized = ast_json_dump_string(json); 158 | std::string ret(serialized ? serialized : ""); 159 | ast_json_free(serialized); 160 | 161 | ast_json_unref(json); 162 | 163 | return ret; 164 | } 165 | static std::string build_payload(const std::string &issuer, const std::string &subject, const std::string &audience, int64_t expires_at) 166 | { 167 | double expires_at_value = expires_at; 168 | struct ast_json *json = ast_json_pack( 169 | "{s: s, s: s, s: s, s: f}", 170 | "iss", issuer.c_str(), 171 | "sub", subject.c_str(), 172 | "aud", audience.c_str(), 173 | "exp", expires_at_value); 174 | if (!json) 175 | return ""; 176 | 177 | char *serialized = ast_json_dump_string(json); 178 | std::string ret(serialized ? serialized : ""); 179 | ast_json_free(serialized); 180 | 181 | ast_json_unref(json); 182 | 183 | return ret; 184 | } 185 | 186 | 187 | std::string GenerateJWT(const std::string &api_key, const std::string &secret_key, 188 | const std::string &issuer, const std::string &subject, const std::string &audience, 189 | int64_t expiration_time_sec) 190 | { 191 | std::string jwt; 192 | 193 | std::string header_bytes = build_header(api_key); 194 | std::string payload_bytes = build_payload(issuer, subject, audience, expiration_time_sec); 195 | 196 | std::string data = base64_encode_url_safe(header_bytes) + "." + base64_encode_url_safe(payload_bytes); 197 | 198 | std::string secret_decoded = base64_decode(secret_key); 199 | 200 | unsigned char sig[SHA256_DIGEST_LENGTH]; 201 | unsigned int sig_len; 202 | unsigned char *ret = HMAC(EVP_sha256(), secret_decoded.data(), secret_decoded.size(), (const unsigned char *) data.data(), data.size(), sig, &sig_len); 203 | if (!ret) 204 | return jwt; 205 | std::string signature = std::string(reinterpret_cast(sig), sig_len); 206 | std::string signature_b64 = base64_encode_url_safe(signature); 207 | jwt = data + "." + signature_b64; 208 | 209 | return jwt; 210 | } 211 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/jwt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef JWT_H 20 | #define JWT_H 21 | 22 | #include 23 | #include 24 | 25 | std::string GenerateJWT( 26 | const std::string &api_key, 27 | const std::string &secret_key, 28 | const std::string &issuer, 29 | const std::string &subject, 30 | const std::string &audience, 31 | int64_t expiration_time_sec); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common wrapper for a few potentially missing GNU programs. 3 | 4 | scriptversion=2012-06-26.16; # UTC 5 | 6 | # Copyright (C) 1996-2013 Free Software Foundation, Inc. 7 | # Originally written by Fran,cois Pinard , 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=http://www.perl.org/ 105 | flex_URL=http://flex.sourceforge.net/ 106 | gnu_software_URL=http://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'automa4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'write-file-hooks 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/samples/grpctts.conf.sample: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | ;Text-To-Speech host:port 4 | endpoint=domain.org:443 5 | 6 | ;Use external CA file (relative to configuration directory). Default: built-in CA 7 | ca_file=grpctts_ca.pem 8 | 9 | ;Speaking rate. Default: 1.0 10 | speaking_rate=2.0 11 | 12 | ;Pitch (currently unused). Default: 0.0 13 | pitch=0.0 14 | 15 | ;Volume gain in decibels (currently unused). Default: 0.0 16 | volume_gain_db=0.3 17 | 18 | ;Voice language code. Default: "" 19 | voice_language_code= 20 | 21 | ;Voice name. Default: "" 22 | voice_name=johnny 23 | 24 | ;Voice gender. Allowed values are "male", "female", "neutral" and "unspecified" ("" is for "unspecified"). Default: "unspecified" 25 | voice_gender=male 26 | 27 | ;Remote audio format. Allowed values are "slin" and "opus". Default: "slin" 28 | remote_frame_format=opus 29 | 30 | 31 | [authorization] 32 | 33 | ;Set API key for authorization. Default: "" 34 | api_key=abcdefghijklmbhopABCDEFGhijlLMNOPQRSTUVWXYZ=TEST 35 | 36 | ;Set secret key for authorization. Default: "" 37 | secret_key=abcdefghijklmbhopABCDEFGhijlLMNOPQRSTUVWXYZ3211123 38 | 39 | ;Set issuer for authorization. Default: "" 40 | issuer=issuer 41 | 42 | ;Set subject for authorization. Default: "" 43 | subject=subject 44 | 45 | ;Set audience for authorization. Default: "" 46 | audience=tinkoff.cloud.tts 47 | 48 | 49 | [buffering] 50 | 51 | ;Set minimal buffer size before playback start as fraction + seconds. Default: "0s" 52 | ;Three forms are allowed: fraction (e. g., "30.3%", "25%"), seconds (e. g., "2s", "1.2s") and fractions+seconds (e. g., "25%+3.3s"). 53 | initial_buffer_size=30%+3s 54 | 55 | ;Set starvation policy. Allowed values: "wait" (all samples are played), "dropout" (real-time mode since playback start) and "abandon" (cancels task in queue). Default: "wait" 56 | starvation_policy=wait 57 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/stream_layers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | #ifndef STREAM_LAYERS_H 20 | #define STREAM_LAYERS_H 21 | 22 | #define typeof __typeof__ 23 | 24 | #include "grpctts.h" 25 | #include "grpctts_conf.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | #include 31 | #ifdef __cplusplus 32 | }; 33 | #endif 34 | #include 35 | 36 | 37 | struct grpctts_channel; 38 | struct grpctts_job; 39 | 40 | 41 | struct stream_source_file { 42 | struct ast_filestream *filestream; 43 | struct ast_frame *buffered_frame; 44 | int buffered_frame_off; 45 | }; 46 | 47 | struct stream_source_sleep { 48 | int sample_count; 49 | }; 50 | 51 | struct stream_source_synthesis { 52 | struct ast_channel *chan; 53 | struct grpctts_job *job; 54 | struct ast_frame *buffered_frame; 55 | int buffered_frame_off; 56 | enum grpctts_starvation_policy starvation_policy; 57 | struct grpctts_buffer_size initial_buffer_size; 58 | size_t dropout_frames; 59 | size_t initial_buffer_samples; 60 | int initial_buffer_reached; 61 | int duration_announced; 62 | }; 63 | 64 | enum stream_source_type { 65 | STREAM_SOURCE_NONE = 0, 66 | STREAM_SOURCE_FILE = 1, 67 | STREAM_SOURCE_SLEEP = 2, 68 | STREAM_SOURCE_SYNTHESIS = 3, 69 | }; 70 | 71 | struct stream_source { 72 | enum stream_source_type type; 73 | union { 74 | struct stream_source_file file; 75 | struct stream_source_sleep sleep; 76 | struct stream_source_synthesis synthesis; 77 | } source; 78 | }; 79 | 80 | struct stream_job { 81 | char *command; 82 | struct stream_job *next; 83 | }; 84 | 85 | struct stream_layer { 86 | int override; 87 | struct stream_job *jobs; 88 | struct stream_job *last_job; 89 | struct stream_source source; 90 | }; 91 | 92 | struct stream_state { 93 | struct ast_channel *chan; 94 | struct grpctts_channel *tts_channel; 95 | struct grpctts_job_conf job_conf; 96 | int efd; 97 | struct timespec next_frame_time; 98 | }; 99 | 100 | extern void stream_layers_global_init(void); 101 | 102 | extern void stream_layers_global_uninit(void); 103 | 104 | extern void stream_layer_init( 105 | struct stream_layer *layer); 106 | 107 | extern void stream_layer_uninit( 108 | struct stream_layer *layer); 109 | 110 | extern void stream_layer_add_job( 111 | struct stream_layer *layer, 112 | const char *command); 113 | 114 | extern void stream_layer_override( 115 | struct stream_layer *layer); 116 | 117 | extern void stream_state_init( 118 | struct stream_state *state, 119 | struct ast_channel *chan, 120 | int efd); 121 | 122 | extern void stream_state_uninit( 123 | struct stream_state *state); 124 | 125 | /* Returns: -1 on error or hangup; 0 on stream finished; 1 on efd triggered */ 126 | extern int stream_layers( 127 | struct stream_state *state, 128 | struct stream_layer *layers, 129 | int layer_count); 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /asterisk_modules/app_playbackground/tts.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package tinkoff.cloud.tts.v1; 4 | option go_package = "github.com/TinkoffCreditSystems/voicekit-examples/golang/pkg/tinkoff/cloud/tts/v1"; 5 | option objc_class_prefix = 'TVKSS'; 6 | 7 | import "google/api/annotations.proto"; 8 | 9 | service TextToSpeech { // Speech synthesis. 10 | rpc ListVoices(ListVoicesRequest) returns (ListVoicesResponses) { // Not implemented Method for retrieving available voice list. 11 | option (google.api.http) = { 12 | get: "/v1/tts:list_voices" 13 | }; 14 | } 15 | rpc Synthesize(SynthesizeSpeechRequest) returns (SynthesizeSpeechResponse) { // Not implemented Method for fragment synthesis. 16 | option (google.api.http) = { 17 | post: "/v1/tts:synthesize" 18 | body: "*" 19 | }; 20 | } 21 | rpc StreamingSynthesize(SynthesizeSpeechRequest) returns (stream StreamingSynthesizeSpeechResponse); // Method for streaming synthesis. 22 | } 23 | 24 | enum SsmlVoiceGender { // Gender of preferred voice to be used for synthesis. 25 | SSML_VOICE_GENDER_UNSPECIFIED = 0; // Unspecified 26 | MALE = 1; // Male 27 | FEMALE = 2; // Female 28 | NEUTRAL = 3; // Neutral 29 | } 30 | 31 | enum AudioEncoding { // Audio encoding. Defines both codec and container. 32 | ENCODING_UNSPECIFIED = 0; // Unspecified - invalid value. Used as default value to avoid accidental errors. 33 | LINEAR16 = 1; // Raw PCM with signed integer 16-bit linear audio samples. 34 | reserved "FLAC"; reserved 2; 35 | MULAW = 3; // Raw PCM with Mu-law mapped 8-bit audio samples (aka PCMU). 36 | reserved "AMR"; reserved 4; 37 | reserved "AMR_WB"; reserved 5; 38 | reserved "OGG_OPUS"; reserved 6; 39 | reserved "SPEEX_WITH_HEADER_BYTE"; reserved 7; 40 | ALAW = 8; // Raw PCM with A-law mapped 8-bit audio samples (aka PCMA). 41 | reserved "LINEAR32F"; reserved 9; // Deprecated. 42 | reserved "OGG_VORBIS"; reserved 10; 43 | RAW_OPUS = 11; // Opus frames packed into Protobuf messages. NOTE: each Opus frame is packed into separate message with "audio_content" field. I. e., you can't just concatenate encoded Opus frames and push it as a single chuk into Opus decoder. Also although Opus is sample rate agnostic, estimated duration of synthesized audio is calculated in samples of specified sample rate. 44 | reserved "MPEG_AUDIO"; reserved 12; 45 | } 46 | 47 | message Voice { // Voice description obtained by available voice request. 48 | repeated string language_codes = 1; // Code of voice spoken language. 49 | string name = 2; // Voice name. 50 | SsmlVoiceGender ssml_gender = 3; // Gender of voice. 51 | int32 natural_sample_rate_hertz = 4; // Original sample rate. I. e., bigger value indicates higher quality. 52 | } 53 | 54 | message ListVoicesRequest { // Request for available voices. 55 | string language_code = 1; // Code of language to get list of voices for. 56 | } 57 | 58 | message ListVoicesResponses { // Response with voice list. 59 | repeated Voice voices = 1; // Array of voices. 60 | } 61 | 62 | message SynthesisInput { // Synthesis input. 63 | string text = 1; // Raw text to synthesize. 64 | string ssml = 2; // The SSML document to synthesize. Only tag is supported for now. 65 | } 66 | 67 | message VoiceSelectionParams { // Configuration of preferred synthesis voice. 68 | string language_code = 1; // Code of preferrable voice spoken language. 69 | string name = 2; // Exact voice name. 70 | SsmlVoiceGender ssml_gender = 3; // Preferrable voice gender. 71 | } 72 | 73 | message AudioConfig { // Configuration of synthesized audio. 74 | AudioEncoding audio_encoding = 1; // Audio encoding. Specifies both container and codec. Must be specified explicity. 75 | double speaking_rate = 2; // Currently ignored. Speaking rate of generated audio as a fraction of original speaking rate. Default value is "1.0". 76 | reserved "pitch"; reserved 3; 77 | reserved "volume_gain_db"; reserved 4; 78 | int32 sample_rate_hertz = 5; // Sample rate of generated audio in Hertz. Must be specified explicity. 79 | } 80 | 81 | message SynthesizeSpeechRequest { // Request using SynthesizeSpeech method. 82 | SynthesisInput input = 1; // Input to synthesize. 83 | VoiceSelectionParams voice = 2; // Voice selection parameters. 84 | AudioConfig audio_config = 3; // Audio configuration. 85 | } 86 | 87 | message SynthesizeSpeechResponse { // Response using SynthesizeSpeech method. 88 | bytes audio_content = 1; // Whole synthesized audio. 89 | } 90 | 91 | message StreamingSynthesizeSpeechResponse { // Chunk of synthesized audio using StreamingSynthesize method. See AudioEncoding = RAW_OPUS for specificity of raw Opus encoding. 92 | bytes audio_chunk = 1; // Chunk of synthesized audio: either samples for LINEAR16, MULAW and ALAW or single frame for RAW_OPUS. 93 | } 94 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/AUTHORS: -------------------------------------------------------------------------------- 1 | Grigoriy Okopnik 2 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/app_waitevent/ChangeLog -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/INSTALL: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | ``` 5 | ./configure --prefix=PREFIX 6 | make 7 | make install 8 | ``` 9 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/Makefile.am: -------------------------------------------------------------------------------- 1 | pkgdir = $(asteriskmoduledir) 2 | pkg_LTLIBRARIES = app_waitevent.la 3 | app_waitevent_la_SOURCES = \ 4 | app_waitevent.c 5 | 6 | app_waitevent_la_CFLAGS = -Wall -O3 -Werror=implicit-function-declaration -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -fPIC -DAST_MODULE=\"app_waitevent\" \ 7 | -DASTERISK_MODULE_VERSION_STRING=\"`git describe --tags --always`\" 8 | app_waitevent_la_CXXFLAGS = -Wall -O3 -std=c++11 -fPIC 9 | app_waitevent_la_LDFLAGS = -Wl,-E -pthread -g -module -avoid-version -ldl 10 | app_waitevent_la_LIBTOOLFLAGS = --tag=disable-static 11 | 12 | 13 | XMLDOC_FILES = app_waitevent.c 14 | 15 | all-local: .xmldocs/app_waitevent-en_US.xml 16 | 17 | .xmldocs/app_waitevent-en_US.xml_tmp: $(XMLDOC_FILES) 18 | mkdir -p .xmldocs 19 | @echo "Generating $@..." 20 | @echo "" > $@ 21 | @echo "" >> $@ 22 | @echo "" >> $@ 23 | @for i in $(XMLDOC_FILES); do \ 24 | $(AWK) -f awk_get_documentation $$i >> $@ ; \ 25 | done ; 26 | @echo "" >> $@ 27 | @echo "Generating $@ done" 28 | 29 | .xmldocs/app_waitevent-en_US.xml: $(XMLDOC_FILES) 30 | echo $@_tmp 31 | make $@_tmp || (rm -f $@_tmp; false) 32 | mv $@_tmp $@ 33 | 34 | install-data-local: .xmldocs/app_waitevent-en_US.xml 35 | test -d $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty || $(mkinstalldirs) $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 36 | $(INSTALL) -m 644 .xmldocs/app_waitevent-en_US.xml $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 37 | 38 | 39 | clobber: distclean 40 | rm -rf ./{configure,aclocal.m4,libtool,ltmain.sh,autom4te.cache,config.h.in,Makefile.in,.xmldocs} 41 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/app_waitevent/NEWS -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/README: -------------------------------------------------------------------------------- 1 | See INSTALL 2 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/awk_get_documentation: -------------------------------------------------------------------------------- 1 | /\/\*\*\* DOCUMENTATION/ {printit=1; next} 2 | /\*\*\*\// {if (printit) exit} 3 | {if (printit) print} 4 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | libtoolize 4 | aclocal 5 | autoheader 6 | automake 7 | autoconf 8 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Wrapper for compilers which do not understand '-c -o'. 3 | 4 | scriptversion=2012-10-14.11; # UTC 5 | 6 | # Copyright (C) 1999-2013 Free Software Foundation, Inc. 7 | # Written by Tom Tromey . 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # This file is maintained in Automake, please report 28 | # bugs to or send patches to 29 | # . 30 | 31 | nl=' 32 | ' 33 | 34 | # We need space, tab and new line, in precisely that order. Quoting is 35 | # there to prevent tools from complaining about whitespace usage. 36 | IFS=" "" $nl" 37 | 38 | file_conv= 39 | 40 | # func_file_conv build_file lazy 41 | # Convert a $build file to $host form and store it in $file 42 | # Currently only supports Windows hosts. If the determined conversion 43 | # type is listed in (the comma separated) LAZY, no conversion will 44 | # take place. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv/,$2, in 65 | *,$file_conv,*) 66 | ;; 67 | mingw/*) 68 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 69 | ;; 70 | cygwin/*) 71 | file=`cygpath -m "$file" || echo "$file"` 72 | ;; 73 | wine/*) 74 | file=`winepath -w "$file" || echo "$file"` 75 | ;; 76 | esac 77 | ;; 78 | esac 79 | } 80 | 81 | # func_cl_dashL linkdir 82 | # Make cl look for libraries in LINKDIR 83 | func_cl_dashL () 84 | { 85 | func_file_conv "$1" 86 | if test -z "$lib_path"; then 87 | lib_path=$file 88 | else 89 | lib_path="$lib_path;$file" 90 | fi 91 | linker_opts="$linker_opts -LIBPATH:$file" 92 | } 93 | 94 | # func_cl_dashl library 95 | # Do a library search-path lookup for cl 96 | func_cl_dashl () 97 | { 98 | lib=$1 99 | found=no 100 | save_IFS=$IFS 101 | IFS=';' 102 | for dir in $lib_path $LIB 103 | do 104 | IFS=$save_IFS 105 | if $shared && test -f "$dir/$lib.dll.lib"; then 106 | found=yes 107 | lib=$dir/$lib.dll.lib 108 | break 109 | fi 110 | if test -f "$dir/$lib.lib"; then 111 | found=yes 112 | lib=$dir/$lib.lib 113 | break 114 | fi 115 | if test -f "$dir/lib$lib.a"; then 116 | found=yes 117 | lib=$dir/lib$lib.a 118 | break 119 | fi 120 | done 121 | IFS=$save_IFS 122 | 123 | if test "$found" != yes; then 124 | lib=$lib.lib 125 | fi 126 | } 127 | 128 | # func_cl_wrapper cl arg... 129 | # Adjust compile command to suit cl 130 | func_cl_wrapper () 131 | { 132 | # Assume a capable shell 133 | lib_path= 134 | shared=: 135 | linker_opts= 136 | for arg 137 | do 138 | if test -n "$eat"; then 139 | eat= 140 | else 141 | case $1 in 142 | -o) 143 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 144 | eat=1 145 | case $2 in 146 | *.o | *.[oO][bB][jJ]) 147 | func_file_conv "$2" 148 | set x "$@" -Fo"$file" 149 | shift 150 | ;; 151 | *) 152 | func_file_conv "$2" 153 | set x "$@" -Fe"$file" 154 | shift 155 | ;; 156 | esac 157 | ;; 158 | -I) 159 | eat=1 160 | func_file_conv "$2" mingw 161 | set x "$@" -I"$file" 162 | shift 163 | ;; 164 | -I*) 165 | func_file_conv "${1#-I}" mingw 166 | set x "$@" -I"$file" 167 | shift 168 | ;; 169 | -l) 170 | eat=1 171 | func_cl_dashl "$2" 172 | set x "$@" "$lib" 173 | shift 174 | ;; 175 | -l*) 176 | func_cl_dashl "${1#-l}" 177 | set x "$@" "$lib" 178 | shift 179 | ;; 180 | -L) 181 | eat=1 182 | func_cl_dashL "$2" 183 | ;; 184 | -L*) 185 | func_cl_dashL "${1#-L}" 186 | ;; 187 | -static) 188 | shared=false 189 | ;; 190 | -Wl,*) 191 | arg=${1#-Wl,} 192 | save_ifs="$IFS"; IFS=',' 193 | for flag in $arg; do 194 | IFS="$save_ifs" 195 | linker_opts="$linker_opts $flag" 196 | done 197 | IFS="$save_ifs" 198 | ;; 199 | -Xlinker) 200 | eat=1 201 | linker_opts="$linker_opts $2" 202 | ;; 203 | -*) 204 | set x "$@" "$1" 205 | shift 206 | ;; 207 | *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) 208 | func_file_conv "$1" 209 | set x "$@" -Tp"$file" 210 | shift 211 | ;; 212 | *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) 213 | func_file_conv "$1" mingw 214 | set x "$@" "$file" 215 | shift 216 | ;; 217 | *) 218 | set x "$@" "$1" 219 | shift 220 | ;; 221 | esac 222 | fi 223 | shift 224 | done 225 | if test -n "$linker_opts"; then 226 | linker_opts="-link$linker_opts" 227 | fi 228 | exec "$@" $linker_opts 229 | exit 1 230 | } 231 | 232 | eat= 233 | 234 | case $1 in 235 | '') 236 | echo "$0: No command. Try '$0 --help' for more information." 1>&2 237 | exit 1; 238 | ;; 239 | -h | --h*) 240 | cat <<\EOF 241 | Usage: compile [--help] [--version] PROGRAM [ARGS] 242 | 243 | Wrapper for compilers which do not understand '-c -o'. 244 | Remove '-o dest.o' from ARGS, run PROGRAM with the remaining 245 | arguments, and rename the output as expected. 246 | 247 | If you are trying to build a whole package this is not the 248 | right script to run: please start by reading the file 'INSTALL'. 249 | 250 | Report bugs to . 251 | EOF 252 | exit $? 253 | ;; 254 | -v | --v*) 255 | echo "compile $scriptversion" 256 | exit $? 257 | ;; 258 | cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) 259 | func_cl_wrapper "$@" # Doesn't return... 260 | ;; 261 | esac 262 | 263 | ofile= 264 | cfile= 265 | 266 | for arg 267 | do 268 | if test -n "$eat"; then 269 | eat= 270 | else 271 | case $1 in 272 | -o) 273 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 274 | # So we strip '-o arg' only if arg is an object. 275 | eat=1 276 | case $2 in 277 | *.o | *.obj) 278 | ofile=$2 279 | ;; 280 | *) 281 | set x "$@" -o "$2" 282 | shift 283 | ;; 284 | esac 285 | ;; 286 | *.c) 287 | cfile=$1 288 | set x "$@" "$1" 289 | shift 290 | ;; 291 | *) 292 | set x "$@" "$1" 293 | shift 294 | ;; 295 | esac 296 | fi 297 | shift 298 | done 299 | 300 | if test -z "$ofile" || test -z "$cfile"; then 301 | # If no '-o' option was seen then we might have been invoked from a 302 | # pattern rule where we don't need one. That is ok -- this is a 303 | # normal compilation that the losing compiler can handle. If no 304 | # '.c' file was seen then we are probably linking. That is also 305 | # ok. 306 | exec "$@" 307 | fi 308 | 309 | # Name of file we expect compiler to create. 310 | cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` 311 | 312 | # Create the lock directory. 313 | # Note: use '[/\\:.-]' here to ensure that we don't use the same name 314 | # that we are using for the .o file. Also, base the name on the expected 315 | # object file name, since that is what matters with a parallel build. 316 | lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d 317 | while true; do 318 | if mkdir "$lockdir" >/dev/null 2>&1; then 319 | break 320 | fi 321 | sleep 1 322 | done 323 | # FIXME: race condition here if user kills between mkdir and trap. 324 | trap "rmdir '$lockdir'; exit 1" 1 2 15 325 | 326 | # Run the compile. 327 | "$@" 328 | ret=$? 329 | 330 | if test -f "$cofile"; then 331 | test "$cofile" = "$ofile" || mv "$cofile" "$ofile" 332 | elif test -f "${cofile}bj"; then 333 | test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" 334 | fi 335 | 336 | rmdir "$lockdir" 337 | exit $ret 338 | 339 | # Local Variables: 340 | # mode: shell-script 341 | # sh-indentation: 2 342 | # eval: (add-hook 'write-file-hooks 'time-stamp) 343 | # time-stamp-start: "scriptversion=" 344 | # time-stamp-format: "%:y-%02m-%02d.%02H" 345 | # time-stamp-time-zone: "UTC" 346 | # time-stamp-end: "; # UTC" 347 | # End: 348 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([app_waitevent], [0.1]) 2 | AC_CONFIG_FILES([Makefile]) 3 | 4 | AC_CONFIG_HEADERS([config.h]) 5 | AM_INIT_AUTOMAKE 6 | AC_ENABLE_SHARED 7 | AC_DISABLE_STATIC 8 | AC_PROG_CC 9 | AC_PROG_LIBTOOL 10 | AC_LIBTOOL_SETUP 11 | 12 | 13 | asteriskmoduledir="$libdir/asterisk/modules" 14 | AC_SUBST(asteriskmoduledir) 15 | 16 | AC_ARG_WITH([asterisk-xmldoc-dir], 17 | [AS_HELP_STRING([--with-asterisk-xmldoc-dir=DIR], 18 | [Asterisk XML documentation directory @<:@default=$localstatedir/lib/asterisk/documentation@:>@])], 19 | [asterisk_xmldoc_dir=$withval], 20 | [asterisk_xmldoc_dir=$localstatedir/lib/asterisk/documentation]) 21 | if test -z "$asterisk_xmldoc_dir"; then 22 | AC_MSG_ERROR([Empty directory for '--with-asterisk-xmldoc-dir=' specified]) 23 | fi 24 | AC_SUBST(asterisk_xmldoc_dir) 25 | 26 | AC_OUTPUT 27 | -------------------------------------------------------------------------------- /asterisk_modules/app_waitevent/missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common wrapper for a few potentially missing GNU programs. 3 | 4 | scriptversion=2012-06-26.16; # UTC 5 | 6 | # Copyright (C) 1996-2013 Free Software Foundation, Inc. 7 | # Originally written by Fran,cois Pinard , 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=http://www.perl.org/ 105 | flex_URL=http://flex.sourceforge.net/ 106 | gnu_software_URL=http://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'automa4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'write-file-hooks 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /asterisk_modules/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | (cd thirdparty && ./bootstrap) 4 | find app_* -maxdepth 0 -type d -exec sh -c 'cd {} && ./bootstrap' \; 5 | find func_* -maxdepth 0 -type d -exec sh -c 'cd {} && ./bootstrap' \; 6 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/AUTHORS: -------------------------------------------------------------------------------- 1 | Grigoriy Okopnik 2 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/func_gettimensec/ChangeLog -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/INSTALL: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | ``` 5 | ./configure --prefix=PREFIX 6 | make 7 | make install 8 | ``` 9 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/Makefile.am: -------------------------------------------------------------------------------- 1 | pkgdir = $(asteriskmoduledir) 2 | pkg_LTLIBRARIES = func_gettimensec.la 3 | func_gettimensec_la_SOURCES = \ 4 | func_gettimensec.c 5 | 6 | func_gettimensec_la_CFLAGS = -Wall -O3 -Werror=implicit-function-declaration -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -fPIC -DAST_MODULE=\"func_gettimensec\" 7 | func_gettimensec_la_CXXFLAGS = -Wall -O3 -std=c++11 -fPIC 8 | func_gettimensec_la_LDFLAGS = -Wl,-E -pthread -g -module -avoid-version -ldl 9 | func_gettimensec_la_LIBTOOLFLAGS = --tag=disable-static 10 | 11 | 12 | XMLDOC_FILES = func_gettimensec.c 13 | 14 | all-local: .xmldocs/func_gettimensec-en_US.xml 15 | 16 | .xmldocs/func_gettimensec-en_US.xml_tmp: $(XMLDOC_FILES) 17 | mkdir -p .xmldocs 18 | @echo "Generating $@..." 19 | @echo "" > $@ 20 | @echo "" >> $@ 21 | @echo "" >> $@ 22 | @for i in $(XMLDOC_FILES); do \ 23 | $(AWK) -f awk_get_documentation $$i >> $@ ; \ 24 | done ; 25 | @echo "" >> $@ 26 | @echo "Generating $@ done" 27 | 28 | .xmldocs/func_gettimensec-en_US.xml: $(XMLDOC_FILES) 29 | echo $@_tmp 30 | make $@_tmp || (rm -f $@_tmp; false) 31 | mv $@_tmp $@ 32 | 33 | install-data-local: .xmldocs/func_gettimensec-en_US.xml 34 | test -d $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty || $(mkinstalldirs) $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 35 | $(INSTALL) -m 644 .xmldocs/func_gettimensec-en_US.xml $(DESTDIR)$(asterisk_xmldoc_dir)/thirdparty 36 | 37 | 38 | clobber: distclean 39 | rm -rf ./{configure,aclocal.m4,libtool,ltmain.sh,autom4te.cache,config.h.in,Makefile.in,.xmldocs} 40 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/asterisk-voicekit-modules/361983775a27b561c041b144e0c5699a659a7799/asterisk_modules/func_gettimensec/NEWS -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/README: -------------------------------------------------------------------------------- 1 | See INSTALL 2 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/awk_get_documentation: -------------------------------------------------------------------------------- 1 | /\/\*\*\* DOCUMENTATION/ {printit=1; next} 2 | /\*\*\*\// {if (printit) exit} 3 | {if (printit) print} 4 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | libtoolize 4 | aclocal 5 | autoheader 6 | automake 7 | autoconf 8 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Wrapper for compilers which do not understand '-c -o'. 3 | 4 | scriptversion=2012-10-14.11; # UTC 5 | 6 | # Copyright (C) 1999-2013 Free Software Foundation, Inc. 7 | # Written by Tom Tromey . 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # This file is maintained in Automake, please report 28 | # bugs to or send patches to 29 | # . 30 | 31 | nl=' 32 | ' 33 | 34 | # We need space, tab and new line, in precisely that order. Quoting is 35 | # there to prevent tools from complaining about whitespace usage. 36 | IFS=" "" $nl" 37 | 38 | file_conv= 39 | 40 | # func_file_conv build_file lazy 41 | # Convert a $build file to $host form and store it in $file 42 | # Currently only supports Windows hosts. If the determined conversion 43 | # type is listed in (the comma separated) LAZY, no conversion will 44 | # take place. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv/,$2, in 65 | *,$file_conv,*) 66 | ;; 67 | mingw/*) 68 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 69 | ;; 70 | cygwin/*) 71 | file=`cygpath -m "$file" || echo "$file"` 72 | ;; 73 | wine/*) 74 | file=`winepath -w "$file" || echo "$file"` 75 | ;; 76 | esac 77 | ;; 78 | esac 79 | } 80 | 81 | # func_cl_dashL linkdir 82 | # Make cl look for libraries in LINKDIR 83 | func_cl_dashL () 84 | { 85 | func_file_conv "$1" 86 | if test -z "$lib_path"; then 87 | lib_path=$file 88 | else 89 | lib_path="$lib_path;$file" 90 | fi 91 | linker_opts="$linker_opts -LIBPATH:$file" 92 | } 93 | 94 | # func_cl_dashl library 95 | # Do a library search-path lookup for cl 96 | func_cl_dashl () 97 | { 98 | lib=$1 99 | found=no 100 | save_IFS=$IFS 101 | IFS=';' 102 | for dir in $lib_path $LIB 103 | do 104 | IFS=$save_IFS 105 | if $shared && test -f "$dir/$lib.dll.lib"; then 106 | found=yes 107 | lib=$dir/$lib.dll.lib 108 | break 109 | fi 110 | if test -f "$dir/$lib.lib"; then 111 | found=yes 112 | lib=$dir/$lib.lib 113 | break 114 | fi 115 | if test -f "$dir/lib$lib.a"; then 116 | found=yes 117 | lib=$dir/lib$lib.a 118 | break 119 | fi 120 | done 121 | IFS=$save_IFS 122 | 123 | if test "$found" != yes; then 124 | lib=$lib.lib 125 | fi 126 | } 127 | 128 | # func_cl_wrapper cl arg... 129 | # Adjust compile command to suit cl 130 | func_cl_wrapper () 131 | { 132 | # Assume a capable shell 133 | lib_path= 134 | shared=: 135 | linker_opts= 136 | for arg 137 | do 138 | if test -n "$eat"; then 139 | eat= 140 | else 141 | case $1 in 142 | -o) 143 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 144 | eat=1 145 | case $2 in 146 | *.o | *.[oO][bB][jJ]) 147 | func_file_conv "$2" 148 | set x "$@" -Fo"$file" 149 | shift 150 | ;; 151 | *) 152 | func_file_conv "$2" 153 | set x "$@" -Fe"$file" 154 | shift 155 | ;; 156 | esac 157 | ;; 158 | -I) 159 | eat=1 160 | func_file_conv "$2" mingw 161 | set x "$@" -I"$file" 162 | shift 163 | ;; 164 | -I*) 165 | func_file_conv "${1#-I}" mingw 166 | set x "$@" -I"$file" 167 | shift 168 | ;; 169 | -l) 170 | eat=1 171 | func_cl_dashl "$2" 172 | set x "$@" "$lib" 173 | shift 174 | ;; 175 | -l*) 176 | func_cl_dashl "${1#-l}" 177 | set x "$@" "$lib" 178 | shift 179 | ;; 180 | -L) 181 | eat=1 182 | func_cl_dashL "$2" 183 | ;; 184 | -L*) 185 | func_cl_dashL "${1#-L}" 186 | ;; 187 | -static) 188 | shared=false 189 | ;; 190 | -Wl,*) 191 | arg=${1#-Wl,} 192 | save_ifs="$IFS"; IFS=',' 193 | for flag in $arg; do 194 | IFS="$save_ifs" 195 | linker_opts="$linker_opts $flag" 196 | done 197 | IFS="$save_ifs" 198 | ;; 199 | -Xlinker) 200 | eat=1 201 | linker_opts="$linker_opts $2" 202 | ;; 203 | -*) 204 | set x "$@" "$1" 205 | shift 206 | ;; 207 | *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) 208 | func_file_conv "$1" 209 | set x "$@" -Tp"$file" 210 | shift 211 | ;; 212 | *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) 213 | func_file_conv "$1" mingw 214 | set x "$@" "$file" 215 | shift 216 | ;; 217 | *) 218 | set x "$@" "$1" 219 | shift 220 | ;; 221 | esac 222 | fi 223 | shift 224 | done 225 | if test -n "$linker_opts"; then 226 | linker_opts="-link$linker_opts" 227 | fi 228 | exec "$@" $linker_opts 229 | exit 1 230 | } 231 | 232 | eat= 233 | 234 | case $1 in 235 | '') 236 | echo "$0: No command. Try '$0 --help' for more information." 1>&2 237 | exit 1; 238 | ;; 239 | -h | --h*) 240 | cat <<\EOF 241 | Usage: compile [--help] [--version] PROGRAM [ARGS] 242 | 243 | Wrapper for compilers which do not understand '-c -o'. 244 | Remove '-o dest.o' from ARGS, run PROGRAM with the remaining 245 | arguments, and rename the output as expected. 246 | 247 | If you are trying to build a whole package this is not the 248 | right script to run: please start by reading the file 'INSTALL'. 249 | 250 | Report bugs to . 251 | EOF 252 | exit $? 253 | ;; 254 | -v | --v*) 255 | echo "compile $scriptversion" 256 | exit $? 257 | ;; 258 | cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) 259 | func_cl_wrapper "$@" # Doesn't return... 260 | ;; 261 | esac 262 | 263 | ofile= 264 | cfile= 265 | 266 | for arg 267 | do 268 | if test -n "$eat"; then 269 | eat= 270 | else 271 | case $1 in 272 | -o) 273 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 274 | # So we strip '-o arg' only if arg is an object. 275 | eat=1 276 | case $2 in 277 | *.o | *.obj) 278 | ofile=$2 279 | ;; 280 | *) 281 | set x "$@" -o "$2" 282 | shift 283 | ;; 284 | esac 285 | ;; 286 | *.c) 287 | cfile=$1 288 | set x "$@" "$1" 289 | shift 290 | ;; 291 | *) 292 | set x "$@" "$1" 293 | shift 294 | ;; 295 | esac 296 | fi 297 | shift 298 | done 299 | 300 | if test -z "$ofile" || test -z "$cfile"; then 301 | # If no '-o' option was seen then we might have been invoked from a 302 | # pattern rule where we don't need one. That is ok -- this is a 303 | # normal compilation that the losing compiler can handle. If no 304 | # '.c' file was seen then we are probably linking. That is also 305 | # ok. 306 | exec "$@" 307 | fi 308 | 309 | # Name of file we expect compiler to create. 310 | cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` 311 | 312 | # Create the lock directory. 313 | # Note: use '[/\\:.-]' here to ensure that we don't use the same name 314 | # that we are using for the .o file. Also, base the name on the expected 315 | # object file name, since that is what matters with a parallel build. 316 | lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d 317 | while true; do 318 | if mkdir "$lockdir" >/dev/null 2>&1; then 319 | break 320 | fi 321 | sleep 1 322 | done 323 | # FIXME: race condition here if user kills between mkdir and trap. 324 | trap "rmdir '$lockdir'; exit 1" 1 2 15 325 | 326 | # Run the compile. 327 | "$@" 328 | ret=$? 329 | 330 | if test -f "$cofile"; then 331 | test "$cofile" = "$ofile" || mv "$cofile" "$ofile" 332 | elif test -f "${cofile}bj"; then 333 | test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" 334 | fi 335 | 336 | rmdir "$lockdir" 337 | exit $ret 338 | 339 | # Local Variables: 340 | # mode: shell-script 341 | # sh-indentation: 2 342 | # eval: (add-hook 'write-file-hooks 'time-stamp) 343 | # time-stamp-start: "scriptversion=" 344 | # time-stamp-format: "%:y-%02m-%02d.%02H" 345 | # time-stamp-time-zone: "UTC" 346 | # time-stamp-end: "; # UTC" 347 | # End: 348 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([func_gettimensec], [0.1]) 2 | AC_CONFIG_FILES([Makefile]) 3 | 4 | AC_CONFIG_HEADERS([config.h]) 5 | AM_INIT_AUTOMAKE 6 | AC_ENABLE_SHARED 7 | AC_DISABLE_STATIC 8 | AC_PROG_CC 9 | AC_PROG_LIBTOOL 10 | AC_LIBTOOL_SETUP 11 | 12 | 13 | asteriskmoduledir="$libdir/asterisk/modules" 14 | AC_SUBST(asteriskmoduledir) 15 | 16 | AC_ARG_WITH([asterisk-xmldoc-dir], 17 | [AS_HELP_STRING([--with-asterisk-xmldoc-dir=DIR], 18 | [Asterisk XML documentation directory @<:@default=$localstatedir/lib/asterisk/documentation@:>@])], 19 | [asterisk_xmldoc_dir=$withval], 20 | [asterisk_xmldoc_dir=$localstatedir/lib/asterisk/documentation]) 21 | if test -z "$asterisk_xmldoc_dir"; then 22 | AC_MSG_ERROR([Empty directory for '--with-asterisk-xmldoc-dir=' specified]) 23 | fi 24 | AC_SUBST(asterisk_xmldoc_dir) 25 | 26 | AC_OUTPUT 27 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/func_gettimensec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Asterisk VoiceKit modules 3 | * 4 | * Copyright (c) JSC Tinkoff Bank, 2018 - 2019 5 | * 6 | * Grigoriy Okopnik 7 | * 8 | * See http://www.asterisk.org for more information about 9 | * the Asterisk project. Please do not directly contact 10 | * any of the maintainers of this project for assistance; 11 | * the project provides a web site, mailing lists and IRC 12 | * channels for your use. 13 | * 14 | * This program is free software, distributed under the terms of 15 | * the GNU General Public License Version 2. See the LICENSE file 16 | * at the top of the source tree. 17 | */ 18 | 19 | /*! \file 20 | * 21 | * \brief Function for getting current time 22 | * 23 | * \author Grigoriy Okopnik 24 | * 25 | * \ingroup functions 26 | */ 27 | 28 | /*** MODULEINFO 29 | extended 30 | ***/ 31 | 32 | extern struct ast_module *AST_MODULE_SELF_SYM(void); 33 | #define AST_MODULE_SELF_SYM AST_MODULE_SELF_SYM 34 | 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | 44 | /*** DOCUMENTATION 45 | 46 | 47 | Returns time in seconds with nanosecond precision as real value. 48 | 49 | 50 | 51 | Specifies source CLOCK. 52 | Allowed values are UTC and MONOTONIC. 53 | 54 | 55 | 56 | 57 | Log(Notice,UTC = ${GET_TIME_NSEC(UTC)}); 58 | // e. g., "UTC = 1547817040.192614270" 59 | 60 | 61 | Log(Notice,Monotonic = ${GET_TIME_NSEC(MONOTONIC)}); 62 | // e. g., "Monotonic = 13480206.384741348" 63 | 64 | 65 | 66 | ***/ 67 | 68 | static int afc_gettimensec_exec(struct ast_channel *chan, const char *cmd, char *parse, char *buffer, size_t buflen) 69 | { 70 | struct timespec current_time = {0, 0}; 71 | if (!strcasecmp(parse, "UTC")) { 72 | if (clock_gettime(CLOCK_REALTIME, ¤t_time)) { 73 | ast_log(AST_LOG_ERROR, "Failed to get current time (0.0 returned): %s\n", strerror(errno)); 74 | current_time.tv_sec = 0; 75 | current_time.tv_nsec = 0; 76 | } 77 | } else if (!strcasecmp(parse, "MONOTONIC")) { 78 | if (clock_gettime(CLOCK_MONOTONIC_RAW, ¤t_time)) { 79 | ast_log(AST_LOG_ERROR, "Failed to get monotonic time (0.0 returned): %s\n", strerror(errno)); 80 | current_time.tv_sec = 0; 81 | current_time.tv_nsec = 0; 82 | } 83 | } else { 84 | ast_log(AST_LOG_ERROR, "Unknown clock '%s' (0.0 returned)\n", parse); 85 | } 86 | 87 | sprintf(buffer, "%llu.%09u", (long long int) current_time.tv_sec, (unsigned int) current_time.tv_nsec); 88 | 89 | return 0; 90 | } 91 | 92 | static struct ast_custom_function acf_gettimensec = { 93 | .name = "GET_TIME_NSEC", 94 | .read = afc_gettimensec_exec, 95 | .read_max = 32, 96 | }; 97 | 98 | static int unload_module(void) 99 | { 100 | ast_custom_function_unregister(&acf_gettimensec); 101 | 102 | return 0; 103 | } 104 | 105 | static int load_module(void) 106 | { 107 | return ast_custom_function_register(&acf_gettimensec); 108 | } 109 | 110 | AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Current time function"); 111 | -------------------------------------------------------------------------------- /asterisk_modules/func_gettimensec/missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common wrapper for a few potentially missing GNU programs. 3 | 4 | scriptversion=2012-06-26.16; # UTC 5 | 6 | # Copyright (C) 1996-2013 Free Software Foundation, Inc. 7 | # Originally written by Fran,cois Pinard , 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=http://www.perl.org/ 105 | flex_URL=http://flex.sourceforge.net/ 106 | gnu_software_URL=http://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'automa4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'write-file-hooks 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /asterisk_modules/thirdparty/Makefile: -------------------------------------------------------------------------------- 1 | all: inst/include/grpc/grpc.h inst/lib/libjansson.a 2 | 3 | 4 | inst/include/grpc/grpc.h: 5 | @command -v which &> /dev/null || (echo "Error: no 'which' tool present; please install one"; false) 6 | cd grpc && make -j`nproc` -e prefix=../inst -e EXTRA_CFLAGS="-fPIC -Wno-error" -e EXTRA_CXXFLAGS="-fPIC -Wno-error" -e REQUIRE_CUSTOM_LIBRARIES_opt=1 install-static install-headers install-plugins 7 | cd grpc/third_party/protobuf && make install 8 | 9 | inst/lib/libjansson.a: 10 | cd jansson-2.12 && CFLAGS="${CFLAGS:+${CFLAGS} }-fPIC" ./configure --prefix=`pwd`/../inst --enable-static --disable-shared && make install 11 | 12 | clobber: 13 | rm -rf grpc inst googleapis jansson-2.12 14 | -------------------------------------------------------------------------------- /asterisk_modules/thirdparty/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | (git clone https://github.com/grpc/grpc.git -b v1.15.0 && cd grpc && git submodule update --init --recursive) 4 | patch -p0 -i grpc-build.patch 5 | patch -p0 -i grpc-gettid.patch 6 | patch -p0 -i grpc-transport_fd_pipe.patch 7 | patch -p0 -i grpc-pollset-fix.patch 8 | (git clone https://github.com/googleapis/googleapis.git -b common-protos-1_3_1 && cd googleapis && git submodule update --init --recursive) 9 | curl -L http://www.digip.org/jansson/releases/jansson-2.12.tar.gz|tar xz 10 | -------------------------------------------------------------------------------- /asterisk_modules/thirdparty/grpc-build.patch: -------------------------------------------------------------------------------- 1 | --- grpc/Makefile.orig 2020-02-28 11:33:31.935472607 +0300 2 | +++ grpc/Makefile 2020-07-01 13:14:33.036418044 +0300 3 | @@ -1379,7 +1379,7 @@ 4 | $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a: third_party/protobuf/configure 5 | $(E) "[MAKE] Building protobuf" 6 | $(Q)mkdir -p $(LIBDIR)/$(CONFIG)/protobuf 7 | - $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static $(PROTOBUF_CONFIG_OPTS)) 8 | + $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" CXXFLAGS="-fvisibility=protected -fPIC" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static $(PROTOBUF_CONFIG_OPTS) --prefix="$(PWD)/$(prefix)" --libdir="$(PWD)/$(prefix)/lib") 9 | $(Q)$(MAKE) -C third_party/protobuf clean 10 | $(Q)$(MAKE) -C third_party/protobuf 11 | $(Q)mkdir -p $(BINDIR)/$(CONFIG)/protobuf 12 | @@ -2463,34 +2463,8 @@ 13 | # This prevents proper debugging after running make install. 14 | 15 | strip-static_c: static_c 16 | -ifeq ($(CONFIG),opt) 17 | - $(E) "[STRIP] Stripping libaddress_sorting.a" 18 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libaddress_sorting.a 19 | - $(E) "[STRIP] Stripping libgpr.a" 20 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgpr.a 21 | - $(E) "[STRIP] Stripping libgrpc.a" 22 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc.a 23 | - $(E) "[STRIP] Stripping libgrpc_cronet.a" 24 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_cronet.a 25 | - $(E) "[STRIP] Stripping libgrpc_unsecure.a" 26 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a 27 | -endif 28 | 29 | strip-static_cxx: static_cxx 30 | -ifeq ($(CONFIG),opt) 31 | - $(E) "[STRIP] Stripping libgrpc++.a" 32 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++.a 33 | - $(E) "[STRIP] Stripping libgrpc++_cronet.a" 34 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_cronet.a 35 | - $(E) "[STRIP] Stripping libgrpc++_error_details.a" 36 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_error_details.a 37 | - $(E) "[STRIP] Stripping libgrpc++_reflection.a" 38 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a 39 | - $(E) "[STRIP] Stripping libgrpc++_unsecure.a" 40 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a 41 | - $(E) "[STRIP] Stripping libgrpcpp_channelz.a" 42 | - $(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpcpp_channelz.a 43 | -endif 44 | 45 | strip-shared_c: shared_c 46 | ifeq ($(CONFIG),opt) 47 | -------------------------------------------------------------------------------- /asterisk_modules/thirdparty/grpc-gettid.patch: -------------------------------------------------------------------------------- 1 | --- grpc/src/core/lib/gpr/log_linux.cc.orig 2020-09-07 16:05:13.463776706 +0300 2 | +++ grpc/src/core/lib/gpr/log_linux.cc 2020-09-07 16:04:57.618783862 +0300 3 | @@ -40,7 +40,7 @@ 4 | #include 5 | #include 6 | 7 | -static long gettid(void) { return syscall(__NR_gettid); } 8 | +static long W_gettid(void) { return syscall(__NR_gettid); } 9 | 10 | void gpr_log(const char* file, int line, gpr_log_severity severity, 11 | const char* format, ...) { 12 | @@ -70,7 +70,7 @@ 13 | gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); 14 | struct tm tm; 15 | static __thread long tid = 0; 16 | - if (tid == 0) tid = gettid(); 17 | + if (tid == 0) tid = W_gettid(); 18 | 19 | timer = static_cast(now.tv_sec); 20 | final_slash = strrchr(args->file, '/'); 21 | -------------------------------------------------------------------------------- /asterisk_modules/thirdparty/grpc-pollset-fix.patch: -------------------------------------------------------------------------------- 1 | --- grpc/src/core/lib/iomgr/ev_epollex_linux.cc.orig 2020-07-02 09:32:55.180897021 +0300 2 | +++ grpc/src/core/lib/iomgr/ev_epollex_linux.cc 2020-07-02 09:43:27.820870486 +0300 3 | @@ -1438,17 +1438,21 @@ 4 | break; 5 | } 6 | } 7 | - GPR_ASSERT(i != pss->pollset_count); 8 | - for (; i < pss->pollset_count - 1; i++) { 9 | - pss->pollsets[i] = pss->pollsets[i + 1]; 10 | + bool set_found = i != pss->pollset_count; 11 | + if (set_found) { 12 | + for (; i < pss->pollset_count - 1; i++) { 13 | + pss->pollsets[i] = pss->pollsets[i + 1]; 14 | + } 15 | + pss->pollset_count--; 16 | } 17 | - pss->pollset_count--; 18 | gpr_mu_unlock(&pss->mu); 19 | - gpr_mu_lock(&ps->mu); 20 | - if (0 == --ps->containing_pollset_set_count) { 21 | - pollset_maybe_finish_shutdown(ps); 22 | + if (set_found) { 23 | + gpr_mu_lock(&ps->mu); 24 | + if (0 == --ps->containing_pollset_set_count) { 25 | + pollset_maybe_finish_shutdown(ps); 26 | + } 27 | + gpr_mu_unlock(&ps->mu); 28 | } 29 | - gpr_mu_unlock(&ps->mu); 30 | } 31 | 32 | // add all fds to pollables, and output a new array of unorphaned out_fds 33 | -------------------------------------------------------------------------------- /asterisk_modules/thirdparty/grpc-transport_fd_pipe.patch: -------------------------------------------------------------------------------- 1 | --- grpc/src/core/lib/channel/connected_channel.cc.orig 2019-02-01 16:28:09.046394700 +0300 2 | +++ grpc/src/core/lib/channel/connected_channel.cc 2019-02-01 17:07:44.077750986 +0300 3 | @@ -23,6 +23,7 @@ 4 | #include 5 | #include 6 | #include 7 | +#include 8 | 9 | #include 10 | #include 11 | @@ -36,6 +37,7 @@ 12 | 13 | typedef struct connected_channel_channel_data { 14 | grpc_transport* transport; 15 | + int socket_fd_pass_socket_fd; 16 | } channel_data; 17 | 18 | typedef struct { 19 | @@ -182,6 +184,8 @@ 20 | channel_data* cd = static_cast(elem->channel_data); 21 | GPR_ASSERT(args->is_last); 22 | cd->transport = nullptr; 23 | + const grpc_arg* arg = grpc_channel_args_find(args->channel_args, "socket_fd_pass_socket_fd"); 24 | + cd->socket_fd_pass_socket_fd = arg ? dup(*((int *) arg->value.pointer.p)) : -1; 25 | return GRPC_ERROR_NONE; 26 | } 27 | 28 | @@ -191,6 +195,9 @@ 29 | if (cd->transport) { 30 | grpc_transport_destroy(cd->transport); 31 | } 32 | + if (cd->socket_fd_pass_socket_fd != -1) { 33 | + close(cd->socket_fd_pass_socket_fd); 34 | + } 35 | } 36 | 37 | /* No-op. */ 38 | @@ -217,6 +224,15 @@ 39 | GPR_ASSERT(elem->filter == &grpc_connected_filter); 40 | GPR_ASSERT(cd->transport == nullptr); 41 | cd->transport = static_cast(t); 42 | + if (cd->socket_fd_pass_socket_fd != -1) { 43 | + grpc_endpoint* endpoint = grpc_transport_get_endpoint(static_cast(t)); 44 | + if (endpoint) { 45 | + int transport_fd = dup(grpc_endpoint_get_fd(endpoint)); 46 | + write(cd->socket_fd_pass_socket_fd, &transport_fd, sizeof(int)); 47 | + } 48 | + close(cd->socket_fd_pass_socket_fd); 49 | + cd->socket_fd_pass_socket_fd = -1; 50 | + } 51 | 52 | /* HACK(ctiller): increase call stack size for the channel to make space 53 | for channel data. We need a cleaner (but performant) way to do this, 54 | -------------------------------------------------------------------------------- /container-data/extract_text: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -n 'SET VARIABLE EXTRACTED_TEXT "\"' 4 | echo "$1" | jq -jr ".alternatives[0].transcript" 5 | echo '\""' 6 | -------------------------------------------------------------------------------- /container-mnt/extensions.ael: -------------------------------------------------------------------------------- 1 | context voicekit-demo { 2 | h => { 3 | Log(NOTICE,Hangup); 4 | }; 5 | parrot => { 6 | Answer(); 7 | WaitEventInit(); 8 | GRPCSTTBackground(); // Start listening for recognized text events in background 9 | PlayBackgroundInitGRPCTTS(); 10 | Set(VAR_NAME=say,voice_name=alyona,{"text":"привет"}); 11 | Log(NOTICE,${VAR_NAME}); 12 | PlayBackground(${VAR_NAME}); 13 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 600]); // Hangup after 10 minutes 14 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); 15 | 16 | while (${SLEEP_TIME} > 0) { 17 | WaitEvent(${SLEEP_TIME}); // Waiting for next event no longer than specified timeout 18 | if (${WAITEVENTSTATUS} == SUCCESS) { 19 | switch (${WAITEVENTNAME}) { 20 | case GRPCSTT_UTF8: 21 | // WARNING: Following is a quick hack and shouln't be used at production services 22 | // Either using https://github.com/drivefast/asterisk-res_json module or 23 | // processing events at AMI client is advised 24 | AGI(extract_text,${QUOTE(${WAITEVENTBODY})}); // Using "jq" command line tool to extract spoken text 25 | if (${EXTRACTED_TEXT} != "") { 26 | Log(NOTICE,Extracted text: ${EXTRACTED_TEXT}); 27 | PlayBackground(&say,,{"text":${EXTRACTED_TEXT}}); // Enqueueing spoken text 28 | } 29 | break; 30 | case GRPCSTT_SESSION_FINISHED: 31 | Log(NOTICE,STT session finished: ${WAITEVENTBODY}); 32 | Set(CALL_END_TIME=0); 33 | break; 34 | case PlayBackgroundError: 35 | Log(NOTICE,Synthesis error: ${WAITEVENTBODY}); 36 | Set(CALL_END_TIME=0); 37 | break; 38 | default: 39 | } 40 | } else { 41 | Log(NOTICE,WaitEvent() returned with WAITEVENTSTATUS == ${WAITEVENTSTATUS}); 42 | } 43 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); 44 | } 45 | Hangup(); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /container-mnt/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | endpoint=stt.tinkoff.ru:443 3 | use_ssl=true 4 | frame_format=slin 5 | max_alternatives=1 6 | 7 | [authorization] 8 | api_key=__SET_API_KEY_HERE__ 9 | secret_key=__SET_SECRET_KEY_HERE__ 10 | issuer=asterisk_stt 11 | subject=asterisk_stt 12 | audience=tinkoff.cloud.stt 13 | -------------------------------------------------------------------------------- /container-mnt/grpctts.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | endpoint=tts.tinkoff.ru:443 3 | use_ssl=true 4 | remote_frame_format=opus 5 | 6 | [authorization] 7 | api_key=__SET_API_KEY_HERE__ 8 | secret_key=__SET_SECRET_KEY_HERE__ 9 | issuer=asterisk_tts 10 | subject=asterisk_tts 11 | audience=tinkoff.cloud.tts 12 | 13 | [buffering] 14 | initial_buffer_size=3s 15 | -------------------------------------------------------------------------------- /container-mnt/sip.conf: -------------------------------------------------------------------------------- 1 | [demo-user] 2 | host=dynamic 3 | secret=lBwzDjXAwMs94Sn 4 | type=friend 5 | transport=udp 6 | disallow=all 7 | allow=slin,alaw 8 | qualify=yes 9 | canreinvite=no 10 | insecure=invite,port 11 | context=voicekit-demo 12 | -------------------------------------------------------------------------------- /examples/combined/combined_stt_tts/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // STT and TTS combined. 2 | // First asking for pet name. 3 | // Second saying an offer using received name. 4 | // 5 | // Since there's no simple way to access JSON out-of-the-box 6 | // you'll have to install module for that from https://github.com/drivefast/asterisk-res_json.git 7 | // If installing the module is problematic basically replace function call with dummy value. 8 | 9 | context incoming { 10 | combined_stt_tts => { 11 | Answer(); 12 | WaitEventInit(); // Initialize event queue 13 | PlayBackgroundInitGRPCTTS(); // Initialize synthesis 14 | GRPCSTTBackground(); // Initialize background recognition 15 | 16 | PlayBackground(say,,{"text":"здравствуйте, назовите кличку вашего кота"}); 17 | 18 | Set(PHASE=WAIT_FOR_ALIAS); 19 | 20 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 21 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 22 | while (${SLEEP_TIME} > 0) { // While not timed out yet 23 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 24 | if (${WAITEVENTSTATUS} == SUCCESS) { 25 | switch (${WAITEVENTNAME}) { 26 | case GRPCSTT_UTF8: 27 | switch (${PHASE}) { 28 | case WAIT_FOR_ALIAS: 29 | Set(PET_ALIAS=${JSONELEMENT(WAITEVENTBODY,alternatives/0/transcript)}); 30 | Log(NOTICE,Pet alias is ${PET_ALIAS}); 31 | PlayBackground(say,,{"text":"У нас есть спецпредложение для вашего кота"}); 32 | PlayBackground(&say,,{"text":"${PET_ALIAS} получит курс виртуозного тайского массажа"}); 33 | Set(PHASE=OFFER); 34 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 30]); 35 | break; 36 | case OFFER: 37 | Set(PHASE=EXITING); 38 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 0.2]); 39 | break; 40 | default: 41 | } 42 | break; 43 | default: 44 | } 45 | } 46 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 47 | } 48 | 49 | Hangup(); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /examples/combined/combined_stt_tts/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=1 7 | 8 | 9 | [authorization] 10 | 11 | api_key=PUT_REAL_VALUE_HERE 12 | secret_key=PUT_REAL_VALUE_HERE 13 | issuer=asterisk_stt 14 | subject=asterisk_stt 15 | audience=tinkoff.cloud.stt 16 | -------------------------------------------------------------------------------- /examples/combined/combined_stt_tts/etc/asterisk/grpctts.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | remote_frame_format=opus 6 | 7 | 8 | [authorization] 9 | 10 | api_key=PUT_REAL_VALUE_HERE 11 | secret_key=PUT_REAL_VALUE_HERE 12 | issuer=asterisk_tts 13 | subject=asterisk_tts 14 | audience=tinkoff.cloud.tts 15 | -------------------------------------------------------------------------------- /examples/stt/stt_basic/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Basic STT example. 2 | // Recognize during a minute logging recognition events with level NOTICE. 3 | 4 | context incoming { 5 | stt_basic => { 6 | Answer(); 7 | WaitEventInit(); // Initialize event queue 8 | GRPCSTTBackground(); // Initialize background recognition 9 | 10 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 11 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 12 | while (${SLEEP_TIME} > 0) { // While not timed out yet 13 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 14 | if (${WAITEVENTSTATUS} == SUCCESS) { 15 | Set(CurrentUTCTime=${GET_TIME_NSEC(UTC)}); 16 | Set(CurrentUTCTimeString=${STRFTIME(${CurrentUTCTime},GMT,%FT%T)}.${CurrentUTCTime:-9:}); 17 | switch (${WAITEVENTNAME}) { 18 | case GRPCSTT_X_REQUEST_ID: 19 | Log(NOTICE,[${CurrentUTCTimeString}] x-request-id = ${WAITEVENTBODY}); 20 | break; 21 | case GRPCSTT_UTF8: 22 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(UTF-8 encoded) = ${WAITEVENTBODY}); 23 | break; 24 | default: 25 | } 26 | } else { 27 | Log(NOTICE,WaitEvent() WAITEVENTSTATUS == ${WAITEVENTSTATUS}; ${WAITEVENTBODY}); 28 | } 29 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 30 | } 31 | Hangup(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /examples/stt/stt_basic/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=1 7 | 8 | 9 | [authorization] 10 | 11 | api_key=PUT_REAL_VALUE_HERE 12 | secret_key=PUT_REAL_VALUE_HERE 13 | issuer=asterisk_stt 14 | subject=asterisk_stt 15 | audience=tinkoff.cloud.stt 16 | -------------------------------------------------------------------------------- /examples/stt/stt_detailed/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Same dial-plan as basic STT example (while "grpcstt.conf" has extra fields). 2 | 3 | context incoming { 4 | stt_detailed => { 5 | Answer(); 6 | WaitEventInit(); // Initialize event queue 7 | GRPCSTTBackground(); // Initialize background recognition 8 | 9 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 10 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 11 | while (${SLEEP_TIME} > 0) { // While not timed out yet 12 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 13 | if (${WAITEVENTSTATUS} == SUCCESS) { 14 | Set(CurrentUTCTime=${GET_TIME_NSEC(UTC)}); 15 | Set(CurrentUTCTimeString=${STRFTIME(${CurrentUTCTime},GMT,%FT%T)}.${CurrentUTCTime:-9:}); 16 | switch (${WAITEVENTNAME}) { 17 | case GRPCSTT_X_REQUEST_ID: 18 | Log(NOTICE,[${CurrentUTCTimeString}] x-request-id = ${WAITEVENTBODY}); 19 | break; 20 | case GRPCSTT_UTF8: 21 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(UTF-8 encoded) = ${WAITEVENTBODY}); 22 | break; 23 | default: 24 | } 25 | } else { 26 | Log(NOTICE,WaitEvent() WAITEVENTSTATUS == ${WAITEVENTSTATUS}; ${WAITEVENTBODY}); 27 | } 28 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 29 | } 30 | Hangup(); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /examples/stt/stt_detailed/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=4 7 | interim_rezults=true 8 | 9 | 10 | [authorization] 11 | 12 | api_key=PUT_REAL_VALUE_HERE 13 | secret_key=PUT_REAL_VALUE_HERE 14 | issuer=asterisk_stt 15 | subject=asterisk_stt 16 | audience=tinkoff.cloud.stt 17 | -------------------------------------------------------------------------------- /examples/stt/stt_error_handling/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Same dial-plan as basic STT example (while "grpcstt.conf" has extra fields). 2 | 3 | context incoming { 4 | stt_detailed => { 5 | Answer(); 6 | WaitEventInit(); // Initialize event queue 7 | GRPCSTTBackground(); // Initialize background recognition 8 | 9 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 10 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 11 | while (${SLEEP_TIME} > 0) { // While not timed out yet 12 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 13 | if (${WAITEVENTSTATUS} == SUCCESS) { 14 | Set(CurrentUTCTime=${GET_TIME_NSEC(UTC)}); 15 | Set(CurrentUTCTimeString=${STRFTIME(${CurrentUTCTime},GMT,%FT%T)}.${CurrentUTCTime:-9:}); 16 | switch (${WAITEVENTNAME}) { 17 | case GRPCSTT_X_REQUEST_ID: 18 | Log(NOTICE,[${CurrentUTCTimeString}] x-request-id = ${WAITEVENTBODY}); 19 | break; 20 | case GRPCSTT_UTF8: 21 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(UTF-8 encoded) = ${WAITEVENTBODY}); 22 | break; 23 | case GRPCSTT_SESSION_FINISHED: 24 | Set(ARRAY(STATUS,ERROR_CODE,ERROR_MESSAGE)=${WAITEVENTBODY}); 25 | if (${STATUS} == SUCCESS) { 26 | Log(NOTICE,Session finished successfully); 27 | } else { 28 | Log(NOTICE,Session finished with error ${ERROR_CODE}: ${ERROR_MESSAGE}); 29 | } 30 | break; 31 | default: 32 | } 33 | } else { 34 | Log(NOTICE,WaitEvent() WAITEVENTSTATUS == ${WAITEVENTSTATUS}; ${WAITEVENTBODY}); 35 | } 36 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 37 | } 38 | Hangup(); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /examples/stt/stt_error_handling/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=4 7 | interim_rezults=true 8 | 9 | 10 | [authorization] 11 | 12 | api_key=PUT_REAL_VALUE_HERE 13 | secret_key=PUT_REAL_VALUE_HERE 14 | issuer=asterisk_stt 15 | subject=asterisk_stt 16 | audience=tinkoff.cloud.stt 17 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // STT with playback example. 2 | // Recognize during a minute also handling playback requests. 3 | // CMD_* user events are supposed to be generated with AMI or ARI 4 | 5 | context incoming { 6 | stt_with_playback => { 7 | Answer(); 8 | WaitEventInit(); // Initialize event queue 9 | GRPCSTTBackground(); // Initialize background recognition 10 | 11 | UserEvent(CMD_PLAY,eventbody:greeting); // Start playing "greeting" 12 | UserEvent(CMD_PLAY_NEXT,eventbody:main_speech); // And enqueue "main_speech" to play right after 13 | 14 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 15 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 16 | while (${SLEEP_TIME} > 0) { // While not timed out yet 17 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 18 | if (${WAITEVENTSTATUS} == SUCCESS) { 19 | Set(CurrentUTCTime=${GET_TIME_NSEC(UTC)}); 20 | Set(CurrentUTCTimeString=${STRFTIME(${CurrentUTCTime},GMT,%FT%T)}.${CurrentUTCTime:-9:}); 21 | switch (${WAITEVENTNAME}) { 22 | case GRPCSTT_X_REQUEST_ID: 23 | Log(NOTICE,[${CurrentUTCTimeString}] x-request-id = ${WAITEVENTBODY}); 24 | break; 25 | case GRPCSTT_ASCII: 26 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(ASCII encoded) = ${WAITEVENTBODY}); 27 | break; 28 | case GRPCSTT_UTF8: 29 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(UTF-8 encoded) = ${WAITEVENTBODY}); 30 | break; 31 | case GRPCSTT_SESSION_FINISHED: 32 | // Something went wrong: recognition session closed 33 | Set(ARRAY(STATUS,ERROR_CODE,ERROR_MESSAGE)=${WAITEVENTBODY}); 34 | if (${STATUS} == SUCCESS) { 35 | // Can't actually happen 36 | Log(NOTICE,Session finished successfully); 37 | } else { 38 | Log(NOTICE,Session finished with error ${ERROR_CODE}: ${ERROR_MESSAGE}); 39 | } 40 | PlayBackground(); 41 | Playback(something_went_wrong); 42 | Set(CALL_END_TIME=0); // Breaking out of while loop 43 | break; 44 | case CMD_PLAY: 45 | // Stop currently played file if there is and play new one 46 | PlayBackground(play,,${WAITEVENTBODY}); 47 | break; 48 | case CMD_PLAY_NEXT: 49 | // Enqueue new file to play 50 | PlayBackground(&play,,${WAITEVENTBODY}); 51 | break; 52 | case CMD_STOP: 53 | // Stop current playback 54 | PlayBackground(); 55 | break; 56 | default: 57 | } 58 | } else { 59 | Log(NOTICE,WaitEvent() WAITEVENTSTATUS == ${WAITEVENTSTATUS}; ${WAITEVENTBODY}); 60 | } 61 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 62 | } 63 | Hangup(); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=1 7 | 8 | 9 | [authorization] 10 | 11 | api_key=PUT_REAL_VALUE_HERE 12 | secret_key=PUT_REAL_VALUE_HERE 13 | issuer=asterisk_stt 14 | subject=asterisk_stt 15 | audience=tinkoff.cloud.stt 16 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback_advanced/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // STT with playback example. 2 | // Recognize during a minute also handling playback requests (advanced). 3 | // CMD_* user events are supposed to be generated with AMI or ARI 4 | 5 | context incoming { 6 | stt_with_playback_advanced => { 7 | Answer(); 8 | WaitEventInit(); // Initialize event queue 9 | GRPCSTTBackground(); // Initialize background recognition 10 | 11 | // In real world these events should come from AMI or ARI 12 | UserEvent(CMD_PLAY_CONTROL,eventbody:1@play\,\,long_nature_sound); // Start playing nature sound at channel 1 (as background) 13 | UserEvent(CMD_PLAY_CONTROL,eventbody:play\,\,greeting); // Start playing "greeting.*" file at channel 0 (default channel) 14 | UserEvent(CMD_PLAY_CONTROL,eventbody:&sleep\,\,2); // Enqueue waiting for 2 seconds at channel 0 15 | UserEvent(CMD_PLAY_CONTROL,eventbody:&play\,\,what_shall_we_do); // Enqueue playing "what_shall_we_do.*" file at channel 0 16 | 17 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 18 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 19 | while (${SLEEP_TIME} > 0) { // While not timed out yet 20 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 21 | if (${WAITEVENTSTATUS} == SUCCESS) { 22 | Set(CurrentUTCTime=${GET_TIME_NSEC(UTC)}); 23 | Set(CurrentUTCTimeString=${STRFTIME(${CurrentUTCTime},GMT,%FT%T)}.${CurrentUTCTime:-9:}); 24 | switch (${WAITEVENTNAME}) { 25 | case GRPCSTT_X_REQUEST_ID: 26 | Log(NOTICE,[${CurrentUTCTimeString}] x-request-id = ${WAITEVENTBODY}); 27 | break; 28 | case GRPCSTT_ASCII: 29 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(ASCII encoded) = ${WAITEVENTBODY}); 30 | break; 31 | case GRPCSTT_UTF8: 32 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(UTF-8 encoded) = ${WAITEVENTBODY}); 33 | break; 34 | case GRPCSTT_SESSION_FINISHED: 35 | // Something went wrong: recognition session closed 36 | Set(ARRAY(STATUS,ERROR_CODE,ERROR_MESSAGE)=${WAITEVENTBODY}); 37 | if (${STATUS} == SUCCESS) { 38 | // Can't actually happen 39 | Log(NOTICE,Session finished successfully); 40 | } else { 41 | Log(NOTICE,Session finished with error ${ERROR_CODE}: ${ERROR_MESSAGE}); 42 | } 43 | Set(CALL_END_TIME=0); // Breaking out of while loop 44 | break; 45 | case CMD_PLAY_CONTROL: 46 | // Lets just pass command as is 47 | PlayBackground(${WAITEVENTBODY}); 48 | break; 49 | default: 50 | } 51 | } else { 52 | Log(NOTICE,WaitEvent() WAITEVENTSTATUS == ${WAITEVENTSTATUS}; ${WAITEVENTBODY}); 53 | } 54 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 55 | } 56 | Hangup(); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback_advanced/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=1 7 | 8 | 9 | [authorization] 10 | 11 | api_key=PUT_REAL_VALUE_HERE 12 | secret_key=PUT_REAL_VALUE_HERE 13 | issuer=asterisk_stt 14 | subject=asterisk_stt 15 | audience=tinkoff.cloud.stt 16 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback_ami/README: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | Execute 'ami-request.sh' script during call to Asterisk to generate AMI events: 5 | ``` 6 | sh ami-request.sh HOST PORT AMI_USER AMI_PASSWORD CHANNEL 7 | ``` 8 | 9 | e. g. 10 | ``` 11 | sh ami-request.sh 127.0.0.1 5038 admin password SIP/1234-00000000 12 | ``` 13 | 14 | Note that AMI access have to be configured properly at /etc/asterisk/manager.conf 15 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback_ami/ami-request.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | command -v nc &> /dev/null || (echo "Error: no 'nc' tool present; please install NetCat"; exit 1) 4 | 5 | if [ "$#" -ne 5 ]; then 6 | if [ "$#" -ne 0 ]; then 7 | echo "Invalid number of arguments" 8 | fi 9 | 10 | echo "Usage: $0 HOST PORT USERNAME SECRET CHANNEL" 11 | 12 | exit 1 13 | fi 14 | 15 | HOST="$1" 16 | PORT="$2" 17 | USERNAME="$3" 18 | SECRET="$4" 19 | CHANNEL="$5" 20 | 21 | X="" 22 | 23 | X="${X}Action: Login\r\n" 24 | X="${X}Username: ${USERNAME}\r\n" 25 | X="${X}Secret: ${SECRET}\r\n" 26 | X="${X}\r\n" 27 | 28 | X="${X}Action: Events\r\n" 29 | X="${X}Eventmask: off\r\n" 30 | X="${X}\r\n" 31 | 32 | X="${X}Action: Events\r\n" 33 | X="${X}Eventmask: call,user\r\n" 34 | X="${X}\r\n" 35 | 36 | X="${X}Action: UserEvent\r\n" 37 | X="${X}ActionID: 321\r\n" 38 | X="${X}Channel: ${CHANNEL}\r\n" 39 | X="${X}UserEvent: CMD_PLAY\r\n" 40 | X="${X}EventBody: greeting\r\n" 41 | X="${X}\r\n" 42 | 43 | X="${X}Action: UserEvent\r\n" 44 | X="${X}ActionID: 321\r\n" 45 | X="${X}Channel: ${CHANNEL}\r\n" 46 | X="${X}UserEvent: CMD_PLAY_NEXT\r\n" 47 | X="${X}EventBody: main_speech\r\n" 48 | X="${X}\r\n" 49 | 50 | echo -ne "$X" | nc "$HOST" "$PORT" 51 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback_ami/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // STT with playback example. 2 | // Recognize during a minute also handling playback requests. 3 | // 4 | // !!NOTE!! 5 | // Execute 'ami-request.sh' script during call (see README up above) 6 | 7 | context incoming { 8 | stt_with_playback => { 9 | Answer(); 10 | WaitEventInit(); // Initialize event queue 11 | GRPCSTTBackground(); // Initialize background recognition 12 | 13 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 14 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 15 | while (${SLEEP_TIME} > 0) { // While not timed out yet 16 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 17 | if (${WAITEVENTSTATUS} == SUCCESS) { 18 | Set(CurrentUTCTime=${GET_TIME_NSEC(UTC)}); 19 | Set(CurrentUTCTimeString=${STRFTIME(${CurrentUTCTime},GMT,%FT%T)}.${CurrentUTCTime:-9:}); 20 | switch (${WAITEVENTNAME}) { 21 | case GRPCSTT_X_REQUEST_ID: 22 | Log(NOTICE,[${CurrentUTCTimeString}] x-request-id = ${WAITEVENTBODY}); 23 | break; 24 | case GRPCSTT_ASCII: 25 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(ASCII encoded) = ${WAITEVENTBODY}); 26 | break; 27 | case GRPCSTT_UTF8: 28 | Log(NOTICE,[${CurrentUTCTimeString}] Phrase(UTF-8 encoded) = ${WAITEVENTBODY}); 29 | break; 30 | case GRPCSTT_SESSION_FINISHED: 31 | // Something went wrong: recognition session closed 32 | Set(ARRAY(STATUS,ERROR_CODE,ERROR_MESSAGE)=${WAITEVENTBODY}); 33 | if (${STATUS} == SUCCESS) { 34 | // Can't actually happen 35 | Log(NOTICE,Session finished successfully); 36 | } else { 37 | Log(NOTICE,Session finished with error ${ERROR_CODE}: ${ERROR_MESSAGE}); 38 | } 39 | PlayBackground(); 40 | Playback(something_went_wrong); 41 | Set(CALL_END_TIME=0); // Breaking out of while loop 42 | break; 43 | case CMD_PLAY: 44 | // Stop currently played file if there is and play new one 45 | PlayBackground(play,,${WAITEVENTBODY}); 46 | break; 47 | case CMD_PLAY_NEXT: 48 | // Enqueue new file to play 49 | PlayBackground(&play,,${WAITEVENTBODY}); 50 | break; 51 | case CMD_STOP: 52 | // Stop current playback 53 | PlayBackground(); 54 | break; 55 | default: 56 | } 57 | } else { 58 | Log(NOTICE,WaitEvent() WAITEVENTSTATUS == ${WAITEVENTSTATUS}; ${WAITEVENTBODY}); 59 | } 60 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 61 | } 62 | Hangup(); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /examples/stt/stt_with_playback_ami/etc/asterisk/grpcstt.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | frame_format=slin 6 | max_alternatives=1 7 | 8 | 9 | [authorization] 10 | 11 | api_key=PUT_REAL_VALUE_HERE 12 | secret_key=PUT_REAL_VALUE_HERE 13 | issuer=asterisk_stt 14 | subject=asterisk_stt 15 | audience=tinkoff.cloud.stt 16 | -------------------------------------------------------------------------------- /examples/tts/tts_basic/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Basic TTS example. 2 | // Recognize during a minute logging recognition events with level NOTICE. 3 | 4 | context incoming { 5 | tts_basic => { 6 | Answer(); 7 | WaitEventInit(); // Initialize event queue 8 | PlayBackgroundInitGRPCTTS(); // Initialize synthesis 9 | 10 | PlayBackground(say,,{"text":"пуговица пушкина"}); 11 | 12 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 13 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 14 | while (${SLEEP_TIME} > 0) { // While not timed out yet 15 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 16 | if (${WAITEVENTSTATUS} == SUCCESS) { 17 | switch (${WAITEVENTNAME}) { 18 | case PlayBackgroundFinished: 19 | Log(NOTICE,Finished layer #${WAITEVENTBODY}); 20 | Set(CALL_END_TIME=0); 21 | break; 22 | case PlayBackgroundError: 23 | Set(ARRAY(LAYER,ERROR_MESSAGE)=${WAITEVENTBODY}); 24 | Log(NOTICE,Error at layer #${LAYER}: ${ERROR_MESSAGE}); 25 | Set(CALL_END_TIME=0); 26 | break; 27 | default: 28 | } 29 | } 30 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 31 | } 32 | 33 | PlayBackground(say,,{"text":"ложка горького"}); 34 | 35 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 36 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 37 | while (${SLEEP_TIME} > 0) { // While not timed out yet 38 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 39 | if (${WAITEVENTSTATUS} == SUCCESS) { 40 | switch (${WAITEVENTNAME}) { 41 | case PlayBackgroundFinished: 42 | Log(NOTICE,Finished layer #${WAITEVENTBODY}); 43 | Set(CALL_END_TIME=0); 44 | break; 45 | case PlayBackgroundError: 46 | Set(ARRAY(LAYER,ERROR_MESSAGE)=${WAITEVENTBODY}); 47 | Log(NOTICE,Error at layer #${LAYER}: ${ERROR_MESSAGE}); 48 | Set(CALL_END_TIME=0); 49 | break; 50 | default: 51 | } 52 | } 53 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 54 | } 55 | 56 | Wait(0.2); 57 | 58 | Hangup(); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /examples/tts/tts_basic/etc/asterisk/grpctts.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | remote_frame_format=opus 6 | 7 | 8 | [authorization] 9 | 10 | api_key=PUT_REAL_VALUE_HERE 11 | secret_key=PUT_REAL_VALUE_HERE 12 | issuer=asterisk_tts 13 | subject=asterisk_tts 14 | audience=tinkoff.cloud.tts 15 | -------------------------------------------------------------------------------- /examples/tts/tts_event_driven/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Basic TTS example with single main loop - recommended approach. 2 | // Recognize during a minute logging recognition events with level NOTICE. 3 | 4 | context incoming { 5 | tts_event_driven => { 6 | Answer(); 7 | WaitEventInit(); // Initialize event queue 8 | PlayBackgroundInitGRPCTTS(); // Initialize synthesis 9 | 10 | PlayBackground(say,,{"text":"пуговица пушкина"}); 11 | 12 | Set(CURRENT_PHRASE=1); 13 | 14 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 60]); // Set end time to a minute after now 15 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 16 | while (${SLEEP_TIME} > 0) { // While not timed out yet 17 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 18 | if (${WAITEVENTSTATUS} == SUCCESS) { 19 | switch (${WAITEVENTNAME}) { 20 | case PlayBackgroundFinished: 21 | if (${CURRENT_PHRASE} == 1) { 22 | PlayBackground(say,,{"text":"\u043b\u043e\u0436\u043a\u0430 \u0433\u043e\u0440\u044c\u043a\u043e\u0433\u043e"}); // "ложка горького" 23 | Set(CURRENT_PHRASE=2); 24 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 80]); 25 | } else if (${CURRENT_PHRASE} == 2) { 26 | PlayBackground(say,,{"text":"толстовка толсто0ва"}); 27 | Set(CURRENT_PHRASE=3); 28 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 40]); 29 | } else if (${CURRENT_PHRASE} == 3) { 30 | // To avoid distortion of last frames 31 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 0.2]); 32 | } 33 | break; 34 | default: 35 | } 36 | } 37 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 38 | } 39 | 40 | Hangup(); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /examples/tts/tts_event_driven/etc/asterisk/grpctts.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | remote_frame_format=opus 6 | 7 | 8 | [authorization] 9 | 10 | api_key=PUT_REAL_VALUE_HERE 11 | secret_key=PUT_REAL_VALUE_HERE 12 | issuer=asterisk_tts 13 | subject=asterisk_tts 14 | audience=tinkoff.cloud.tts 15 | -------------------------------------------------------------------------------- /examples/tts/tts_event_driven_ami/README: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | 4 | Execute 'ami-request.sh' script during call to Asterisk to generate AMI events: 5 | ``` 6 | sh ami-request.sh HOST PORT AMI_USER AMI_PASSWORD CHANNEL 7 | ``` 8 | 9 | e. g. 10 | ``` 11 | sh ami-request.sh 127.0.0.1 5038 admin password SIP/1234-00000000 12 | ``` 13 | 14 | Note that AMI access have to be configured properly at /etc/asterisk/manager.conf 15 | -------------------------------------------------------------------------------- /examples/tts/tts_event_driven_ami/ami-request.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | command -v nc &> /dev/null || (echo "Error: no 'nc' tool present; please install NetCat"; exit 1) 4 | 5 | if [ "$#" -ne 5 ]; then 6 | if [ "$#" -ne 0 ]; then 7 | echo "Invalid number of arguments" 8 | fi 9 | 10 | echo "Usage: $0 HOST PORT USERNAME SECRET CHANNEL" 11 | 12 | exit 1 13 | fi 14 | 15 | HOST="$1" 16 | PORT="$2" 17 | USERNAME="$3" 18 | SECRET="$4" 19 | CHANNEL="$5" 20 | 21 | X="" 22 | 23 | X="${X}Action: Login\r\n" 24 | X="${X}Username: ${USERNAME}\r\n" 25 | X="${X}Secret: ${SECRET}\r\n" 26 | X="${X}\r\n" 27 | 28 | X="${X}Action: Events\r\n" 29 | X="${X}Eventmask: off\r\n" 30 | X="${X}\r\n" 31 | 32 | X="${X}Action: Events\r\n" 33 | X="${X}Eventmask: call,user\r\n" 34 | X="${X}\r\n" 35 | 36 | X="${X}Action: UserEvent\r\n" 37 | X="${X}ActionID: 321\r\n" 38 | X="${X}Channel: ${CHANNEL}\r\n" 39 | X="${X}UserEvent: CMD_SAY\r\n" 40 | X="${X}EventBody: {\"text\":\"пуговица пушкина\"}\r\n" 41 | X="${X}\r\n" 42 | 43 | X="${X}Action: UserEvent\r\n" 44 | X="${X}ActionID: 321\r\n" 45 | X="${X}Channel: ${CHANNEL}\r\n" 46 | X="${X}UserEvent: CMD_SAY_NEXT\r\n" 47 | X="${X}EventBody: {\"text\":\"\\u043b\\u043e\\u0436\\u043a\\u0430 \\u0433\\u043e\\u0440\\u044c\\u043a\\u043e\\u0433\\u043e\"}\r\n" 48 | X="${X}\r\n" 49 | 50 | X="${X}Action: UserEvent\r\n" 51 | X="${X}ActionID: 321\r\n" 52 | X="${X}Channel: ${CHANNEL}\r\n" 53 | X="${X}UserEvent: CMD_SAY_NEXT\r\n" 54 | X="${X}EventBody: {\"text\":\"толстовка толсто0ва\"}\r\n" 55 | X="${X}\r\n" 56 | 57 | echo -ne "$X" | nc "$HOST" "$PORT" 58 | -------------------------------------------------------------------------------- /examples/tts/tts_event_driven_ami/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Basic TTS example with single main loop - recommended approach. 2 | // Recognize during a minute logging recognition events with level NOTICE. 3 | // 4 | // !!NOTE!! 5 | // Execute 'ami-request.sh' script during call (see README up above) 6 | 7 | context incoming { 8 | tts_event_driven => { 9 | Answer(); 10 | WaitEventInit(); // Initialize event queue 11 | PlayBackgroundInitGRPCTTS(); // Initialize synthesis 12 | 13 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + 120]); // Set end time to 2 minutes after now 14 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 15 | while (${SLEEP_TIME} > 0) { // While not timed out yet 16 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 17 | if (${WAITEVENTSTATUS} == SUCCESS) { 18 | switch (${WAITEVENTNAME}) { 19 | case CMD_SAY: 20 | PlayBackground(say,,${WAITEVENTBODY}); 21 | break; 22 | case CMD_SAY_NEXT: 23 | PlayBackground(&say,,${WAITEVENTBODY}); 24 | break; 25 | default: 26 | } 27 | } 28 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 29 | } 30 | 31 | Hangup(); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /examples/tts/tts_event_driven_ami/etc/asterisk/grpctts.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | remote_frame_format=opus 6 | 7 | 8 | [authorization] 9 | 10 | api_key=PUT_REAL_VALUE_HERE 11 | secret_key=PUT_REAL_VALUE_HERE 12 | issuer=asterisk_tts 13 | subject=asterisk_tts 14 | audience=tinkoff.cloud.tts 15 | -------------------------------------------------------------------------------- /examples/tts/tts_wait_for_event/etc/asterisk/extensions.ael: -------------------------------------------------------------------------------- 1 | // Basic TTS example. 2 | // Recognize during a minute logging recognition events with level NOTICE. 3 | 4 | context incoming { 5 | tts_wait_for_event => { 6 | Answer(); 7 | WaitEventInit(); // Initialize event queue 8 | PlayBackgroundInitGRPCTTS(); // Initialize synthesis 9 | 10 | PlayBackground(say,,{"text":"пуговица пушкина"}); 11 | 12 | &WaitForEvent(60); 13 | 14 | PlayBackground(say,,{"text":"ложка горького"}); 15 | 16 | &WaitForEvent(70); 17 | 18 | Wait(1); 19 | 20 | Hangup(); 21 | } 22 | }; 23 | macro WaitForEvent(MAX_WAIT_TIME) { 24 | Set(CALL_END_TIME=$[${GET_TIME_NSEC(MONOTONIC)} + ${MAX_WAIT_TIME}]); // Set end time to a minute after now 25 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Calculate maximum wait time 26 | while (${SLEEP_TIME} > 0) { // While not timed out yet 27 | WaitEvent(${SLEEP_TIME}); // Wait until next event or timeout 28 | if (${WAITEVENTSTATUS} == SUCCESS) { 29 | switch (${WAITEVENTNAME}) { 30 | case PlayBackgroundFinished: 31 | Log(NOTICE,Finished layer #${WAITEVENTBODY}); 32 | Set(CALL_END_TIME=0); 33 | break; 34 | case PlayBackgroundError: 35 | Set(ARRAY(LAYER,ERROR_MESSAGE)=${WAITEVENTBODY}); 36 | Log(NOTICE,Error at layer #${LAYER}: ${ERROR_MESSAGE}); 37 | Set(CALL_END_TIME=0); 38 | break; 39 | default: 40 | } 41 | } 42 | Set(SLEEP_TIME=$[${CALL_END_TIME} - ${GET_TIME_NSEC(MONOTONIC)}]); // Update maximum wait time 43 | } 44 | return; 45 | } 46 | -------------------------------------------------------------------------------- /examples/tts/tts_wait_for_event/etc/asterisk/grpctts.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | endpoint=domain.org:443 4 | use_ssl=true 5 | remote_frame_format=opus 6 | 7 | 8 | [authorization] 9 | 10 | api_key=PUT_REAL_VALUE_HERE 11 | secret_key=PUT_REAL_VALUE_HERE 12 | issuer=asterisk_tts 13 | subject=asterisk_tts 14 | audience=tinkoff.cloud.tts 15 | --------------------------------------------------------------------------------