Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adding app_swift-1.4.2.

  • Loading branch information...
commit 50967e796d8700ff9f61e40723791c840dfdec55 0 parents
@eric authored
27 CHANGELOG
@@ -0,0 +1,27 @@
+2008-07-08 - Darren Sessions <dmsessions@gmail.com>
+---------------------------------------------------
+ Added support for handling multiple dtmf input
+
+ Added support for input timeout and max input digits
+
+ Ignores DTMF if no timeout and max digits args are specified
+
+ Can now wait for DTMF after text-to-speech processing is done if the
+ timeout and max digits args are specified
+
+ Internal cleanup
+
+ app_swift 1.4.2 released for the Asterisk 1.4.x code base
+
+
+2006-10-27 - Will Orton <will@loopfree.net>
+-------------------------------------------
+ Update to work with Asterisk v1.4Beta3 (will no longer work with 1.2.x)
+ Internal cleanup
+ app_swift 0.9.1 released
+
+
+2006-10-26 - Will Orton <will@loopfree.net>
+-------------------------------------------
+ app_swift 0.9 initial release
+
281 LICENSE
@@ -0,0 +1,281 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
34 Makefile
@@ -0,0 +1,34 @@
+NAME=app_swift
+CONF=swift.conf
+
+CC=gcc
+OSARCH=$(shell uname -s)
+
+SWIFT_DIR=/opt/swift
+CFLAGS=-I${SWIFT_DIR}/include -g -Wall -D_REENTRANT -D_GNU_SOURCE -fPIC
+LDFLAGS=-L${SWIFT_DIR}/lib -lswift -lm -lswift $(patsubst ${SWIFT_DIR}/lib/lib%.so,-l%,$(wildcard ${SWIFT_DIR}/lib/libcep*.so))
+SOLINK=-shared -Xlinker -x
+
+RES=$(shell if [ -f /usr/include/asterisk/channel.h ]; then echo "$(NAME).so"; fi)
+
+MODULES_DIR=/usr/lib/asterisk/modules
+
+$(NAME).so : $(NAME).o
+ $(CC) $(SOLINK) -o $@ $(LDFLAGS) $<
+
+all: $(RES)
+
+clean:
+ rm -f $(NAME).o $(NAME).so
+
+install: all
+ if ! [ -f /etc/asterisk/$(CONF) ]; then \
+ install -m 644 $(CONF).sample /etc/asterisk/$(CONF) ; \
+ fi
+ if [ -f $(NAME).so ]; then \
+ install -m 755 $(NAME).so $(MODULES_DIR) ; \
+ fi
+
+reload: install
+ asterisk -rx "module unload ${RES}"
+ asterisk -rx "module load ${RES}"
27 README
@@ -0,0 +1,27 @@
+The beauty of this application is its ability to stream the
+data from the text-to-speech engine vs. having to write out
+and possibly convert audio files to the filesystem.
+
+This release is specific to the Asterisk 1.4.x codebase and
+will NOT work with the 1.6.x codebase. Please visit my site
+for 1.6.x compatible releases.
+
+http://www.darrensessions.com
+
+For addition help, type 'core show application swift' on the
+Asterisk command line interface.
+
+Bug reports, comments, or otherwise can be directed to me at
+dmsessions@gmail.com.
+
+Thanks,
+
+- Darren Sessions
+
+
+Installation
+------------
+unpack
+make
+make install
+
575 app_swift.c
@@ -0,0 +1,575 @@
+/*
+ * app_swift -- A Cepstral Swift TTS engine interface
+ *
+ * Copyright (C) 2008, Darren Sessions
+ * Copyright (C) 2006, Will Orton
+ *
+ * Darren Sessions <dmsessions@gmail.com>
+ *
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. Read the LICENSE
+ * file for details.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Cepstral Swift TTS engine interface
+ *
+ * \author Darren Sessions <dmsessions@gmail.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.4.2 $")
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <fcntl.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <swift.h>
+
+#include "asterisk/astobj.h"
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/options.h"
+#include "asterisk/time.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Swift TTS Application";
+
+static char *app = "Swift";
+
+static char *synopsis = "Speak text through Swift text-to-speech engine.";
+
+static char *descrip =" Syntax: Swift(text[|timeout in ms|maximum digits])\n"
+ "Example: Swift(Hello World|5000|5) = 5 second delay between 5 digits\n"
+ "This application operates in two modes. One is processing text-to-speech while\n"
+ "listening for DTMF and the other just processes the text-to-speech while ignoring\n"
+ "DTMF entirely. \n"
+ "Unless the timeout and maximum digits options are BOTH specified, the application\n"
+ "will automatically ignore DTMF.\n"
+ "Returns -1 on hangup or 0 otherwise. \n";
+
+const int framesize = 160 * 4;
+const int samplerate = 8000; // We do uLAW
+
+#define SWIFT_CONFIG_FILE "swift.conf"
+static unsigned int cfg_buffer_size;
+static int cfg_goto_exten;
+static char cfg_voice[20];
+
+// Data shared between is and the swift generating process
+struct stuff {
+ ASTOBJ_COMPONENTS(struct stuff);
+ int generating_done;
+ char *q;
+ char *pq_r; //queue read position
+ char *pq_w; //queue write position
+ int qc;
+ int immediate_exit;
+
+};
+
+
+#define dtmf_codes 12
+
+struct dtmf_lookup
+{
+ long ast_res;
+ char* dtmf_res;
+};
+
+static struct dtmf_lookup ast_dtmf_table[dtmf_codes] =
+{
+ {35, "#"},
+ {42, "*"},
+ {48, "0"},
+ {49, "1"},
+ {50, "2"},
+ {51, "3"},
+ {52, "4"},
+ {53, "5"},
+ {54, "6"},
+ {55, "7"},
+ {56, "8"},
+ {57, "9"}
+};
+
+void swift_init_stuff(struct stuff *ps)
+{
+ ASTOBJ_INIT(ps);
+ ps->generating_done = 0;
+ ps->q = malloc(cfg_buffer_size);
+ ps->pq_r = ps->q;
+ ps->pq_w = ps->q;
+ ps->qc = 0;
+ ps->immediate_exit = 0;
+}
+
+// Returns true if swift is generating speech or we still have some
+// queued up.
+int swift_generator_running(struct stuff *ps)
+{
+ int r;
+ ASTOBJ_RDLOCK(ps);
+ r = !ps->immediate_exit && (!ps->generating_done || ps->qc);
+ ASTOBJ_UNLOCK(ps);
+ return r;
+}
+
+int swift_bytes_available(struct stuff *ps)
+{
+ int r;
+ ASTOBJ_RDLOCK(ps);
+ r = ps->qc;
+ ASTOBJ_UNLOCK(ps);
+ return r;
+}
+
+swift_result_t swift_cb(swift_event *event, swift_event_t type, void *udata)
+{
+ void *buf;
+ int len, spacefree;
+ unsigned long sleepfor;
+ swift_event_t rv = SWIFT_SUCCESS;
+ struct stuff *ps = udata;
+
+ if (type == SWIFT_EVENT_AUDIO) {
+ rv = swift_event_get_audio(event, &buf, &len);
+ if (!SWIFT_FAILED(rv) && len > 0) {
+// ast_log(LOG_DEBUG, "audio callback\n");
+
+ ASTOBJ_WRLOCK(ps);
+
+ // Sleep while waiting for some queue space to become available
+ while (len + ps->qc > cfg_buffer_size && !ps->immediate_exit) {
+ // Each byte is 125us of time, so assume queue space will become available
+ // at that rate and guess when we'll have enough space available.
+ // + another (125 usec/sample * framesize samples) (1 frame) for fudge
+ sleepfor = ((unsigned long)(len - (cfg_buffer_size - ps->qc)) * 125UL) + (125UL * (unsigned long)framesize);
+// ast_log(LOG_DEBUG, "generator: %d bytes to write but only %d space avail, sleeping %ldus\n", len, cfg_buffer_size - ps->qc, sleepfor);
+ ASTOBJ_UNLOCK(ps);
+ usleep(sleepfor);
+ ASTOBJ_WRLOCK(ps);
+ }
+
+ if (ps->immediate_exit)
+ return SWIFT_SUCCESS;
+
+ spacefree = cfg_buffer_size - ((unsigned int) ps->pq_w - (unsigned int)ps->q);
+ if (len > spacefree) {
+// ast_log(LOG_DEBUG, "audio fancy write; %d bytes but only %d avail to end %d totalavail\n", len, spacefree, cfg_buffer_size - ps->qc);
+ //write #1 to end of mem
+ memcpy(ps->pq_w, buf, spacefree);
+ ps->pq_w = ps->q;
+ ps->qc += spacefree;
+
+ //write #2 and beg of mem
+ memcpy(ps->pq_w, buf + spacefree, len - spacefree);
+ ps->pq_w += len - spacefree;
+ ps->qc += len - spacefree;
+
+ } else {
+// ast_log(LOG_DEBUG, "audio easy write, %d avail to end %d totalavail\n", spacefree, cfg_buffer_size - ps->qc);
+ memcpy(ps->pq_w, buf, len);
+ ps->pq_w += len;
+ ps->qc += len;
+ }
+ ASTOBJ_UNLOCK(ps);
+ } else {
+ ast_log(LOG_DEBUG, "got audio callback but get_audio call failed\n");
+ }
+
+ } else if (type == SWIFT_EVENT_END) {
+ ast_log(LOG_DEBUG, "got END callback; done generating audio\n");
+ ASTOBJ_WRLOCK(ps);
+ ps->generating_done = 1;
+ ASTOBJ_UNLOCK(ps);
+ } else {
+ ast_log(LOG_DEBUG, "UNKNOWN callback\n");
+ }
+ return rv;
+}
+
+static int dtmf_conv(int dtmf)
+{
+ char *res = (char *) malloc(100);
+ int dtmf_search_counter = 0, dtmf_search_match = 0;
+
+ while ((dtmf_search_counter < dtmf_codes) && (dtmf_search_match == 0)) {
+ if (dtmf == ast_dtmf_table[dtmf_search_counter].ast_res) {
+ dtmf_search_match = 1;
+ sprintf(res, "%s", ast_dtmf_table[dtmf_search_counter].dtmf_res);
+ }
+ dtmf_search_counter = dtmf_search_counter + 1;
+ }
+
+ return *res;
+}
+
+static int listen_for_dtmf(struct ast_channel *chan, int timeout, int max_digits)
+{
+ char *dtmf_conversion = (char *) malloc(100);
+ char cnv[2];
+ int dtmf, res;
+ int i = 0, loop = 0;
+
+ while (i < max_digits && loop == 0) {
+
+ dtmf = ast_waitfordigit(chan, 5000);
+
+ if (dtmf) {
+ sprintf(cnv, "%c", dtmf_conv(dtmf));
+ if (i > 0) {
+ strcat(dtmf_conversion, cnv);
+ } else {
+ sprintf(dtmf_conversion, "%s", cnv);
+ }
+ i = i + 1;
+ } else {
+ loop = 1;
+ }
+ }
+
+ res = strtol(dtmf_conversion, NULL, 0);
+
+ return res;
+}
+
+static int engine(struct ast_channel *chan, void *data)
+{
+ int res = 0, argc = 0, max_digits = 0, timeout = 0, alreadyran = 0;
+ int ms, len, old_writeformat, availatend, rc;
+ char *argv[3], *parse = NULL, *text = NULL, results[20], tmp_exten[2];
+ struct ast_module_user *u;
+ struct ast_frame *f;
+ struct myframe {
+ struct ast_frame f;
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ unsigned char frdata[framesize];
+ } myf;
+ struct timeval next;
+
+ // Swift TTS engine stuff
+ swift_engine *engine;
+ swift_port *port;
+ swift_voice *voice;
+ swift_params *params;
+ swift_result_t sresult;
+ swift_background_t tts_stream;
+ unsigned int event_mask;
+
+ struct stuff *ps;
+
+ parse = ast_strdupa(data);
+
+ u = ast_module_user_add(chan);
+
+ argc = ast_app_separate_args(parse, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ text = argv[0];
+
+ if (!ast_strlen_zero(argv[1]))
+ timeout = strtol(argv[1], NULL, 0);
+
+ if (!ast_strlen_zero(argv[2]))
+ max_digits = strtol(argv[2], NULL, 0);
+
+ if (ast_strlen_zero(text)) {
+ ast_log(LOG_WARNING, "%s requires text to speak!\n", app);
+ return -1;
+ }
+
+ if (!ast_strlen_zero(text))
+ ast_log(LOG_NOTICE, "Text to Speak : %s\n", text);
+
+ if (timeout > 0)
+ ast_log(LOG_NOTICE, "Timeout : %d\n", timeout);
+
+ if (max_digits > 0)
+ ast_log(LOG_NOTICE, "Max Digits : %d\n", max_digits);
+
+ ps = malloc(sizeof(struct stuff));
+ swift_init_stuff(ps);
+
+ //// Set up synthesis
+
+ if((engine = swift_engine_open(NULL)) == NULL) {
+ ast_log(LOG_ERROR, "Failed to open Swift Engine.\n");
+ goto exception;
+ }
+
+ params = swift_params_new(NULL);
+ swift_params_set_string(params, "audio/encoding", "ulaw");
+ swift_params_set_string(params, "audio/sampling-rate", "8000");
+ swift_params_set_string(params, "audio/output-format", "raw");
+ swift_params_set_string(params, "tts/text-encoding", "utf-8");
+// swift_params_set_float(params, "speech/pitch/shift", 1.0);
+// swift_params_set_int(params, "speech/rate", 150);
+// swift_params_set_int(params, "audio/volume", 110);
+ swift_params_set_int(params, "audio/deadair", 0);
+
+ if((port = swift_port_open(engine, params)) == NULL) {
+ ast_log(LOG_ERROR, "Failed to open Swift Port.\n");
+ goto exception;
+ }
+
+ if ((voice = swift_port_set_voice_by_name(port, cfg_voice)) == NULL) {
+ ast_log(LOG_ERROR, "Failed to set voice.\n");
+ goto exception;
+ }
+
+ event_mask = SWIFT_EVENT_AUDIO | SWIFT_EVENT_END;
+ swift_port_set_callback(port, &swift_cb, event_mask, ps);
+
+ if(SWIFT_FAILED(swift_port_speak_text(port, text, 0, NULL, &tts_stream, NULL))) {
+ ast_log(LOG_ERROR, "Failed to speak.\n");
+ goto exception;
+ }
+
+ if(chan->_state!=AST_STATE_UP)
+ ast_answer(chan);
+ ast_stopstream(chan);
+
+ old_writeformat = chan->writeformat;
+ if(ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format.\n");
+ goto exception;
+ }
+
+ res = 0;
+ // Wait 100 ms first for synthesis to start crankin'; if that's not
+ // enough the
+ next = ast_tvadd(ast_tvnow(), ast_tv(0, 100000));
+
+ while (swift_generator_running(ps)) {
+ ms = ast_tvdiff_ms(next, ast_tvnow());
+ if (ms <= 0) {
+ if (swift_bytes_available(ps) > 0) {
+ ASTOBJ_WRLOCK(ps);
+// ast_log(LOG_DEBUG, "Queue %d bytes, writing a frame\n", ps->qc);
+
+ len = fmin(framesize, ps->qc);
+ availatend = cfg_buffer_size - (ps->pq_r - ps->q);
+ if (len > availatend) {
+// ast_log(LOG_DEBUG, "Fancy read; %d bytes but %d at end, %d free \n", len, availatend, cfg_buffer_size - ps->qc);
+
+ //read #1: to end of q buf
+ memcpy(myf.frdata, ps->pq_r, availatend);
+ ps->qc -= availatend;
+
+ //read #2: reset to start of q buf and get rest
+ ps->pq_r = ps->q;
+ memcpy(myf.frdata + availatend, ps->pq_r, len - availatend);
+ ps->qc -= len - availatend;
+ ps->pq_r += len - availatend;
+
+ } else {
+// ast_log(LOG_DEBUG, "Easy read; %d bytes and %d at end, %d free\n", len, availatend, cfg_buffer_size - ps->qc);
+ memcpy(myf.frdata, ps->pq_r, len);
+ ps->qc -= len;
+ ps->pq_r += len;
+ }
+
+ myf.f.frametype = AST_FRAME_VOICE;
+ myf.f.subclass = AST_FORMAT_ULAW;
+ myf.f.datalen = len;
+ myf.f.samples = len;
+ myf.f.data = myf.frdata;
+ myf.f.mallocd = 0;
+ myf.f.offset = AST_FRIENDLY_OFFSET;
+ myf.f.src = __PRETTY_FUNCTION__;
+ myf.f.delivery.tv_sec = 0;
+ myf.f.delivery.tv_usec = 0;
+ if(ast_write(chan, &myf.f) < 0) {
+ ast_log(LOG_DEBUG, "ast_write failed\n");
+ }
+// ast_log(LOG_DEBUG, "wrote a frame of %d\n", len);
+ if (ps->qc < 0)
+ ast_log(LOG_DEBUG, "queue claims to contain negative bytes. Huh? qc < 0\n");
+ ASTOBJ_UNLOCK(ps);
+ next = ast_tvadd(next, ast_samp2tv(myf.f.samples, samplerate));
+
+ } else {
+ next = ast_tvadd(next, ast_samp2tv(framesize/2, samplerate));
+ ast_log(LOG_DEBUG, "Whoops, writer starved for audio\n");
+ }
+
+ } else {
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0) {
+ ast_log(LOG_DEBUG, "Hangup detected\n");
+ res = -1;
+ ASTOBJ_WRLOCK(ps);
+ ps->immediate_exit = 1;
+ ASTOBJ_UNLOCK(ps);
+
+ } else if (ms) {
+ f = ast_read(chan);
+ if(!f) {
+ ast_log(LOG_DEBUG, "Null frame == hangup() detected\n");
+ res = -1;
+ ASTOBJ_WRLOCK(ps);
+ ps->immediate_exit = 1;
+ ASTOBJ_UNLOCK(ps);
+
+ } else if (f->frametype == AST_FRAME_DTMF && timeout > 0 && max_digits > 0) {
+
+ alreadyran = 1;
+ res = 0;
+
+ ASTOBJ_WRLOCK(ps);
+ ps->immediate_exit = 1;
+ ASTOBJ_UNLOCK(ps);
+
+ if (max_digits > 1) {
+ rc = listen_for_dtmf(chan, timeout, max_digits - 1);
+ }
+
+ if (rc) {
+ sprintf(results, "%c%d", f->subclass, rc);
+ } else {
+ sprintf(results, "%c", f->subclass);
+ }
+
+ ast_log(LOG_NOTICE, "DTMF = %s\n", results);
+ pbx_builtin_setvar_helper(chan, "SWIFT_DTMF", results);
+
+ ast_frfree(f);
+
+ } else { // ignore other frametypes
+ ast_frfree(f);
+ }
+ }
+ }
+
+ ASTOBJ_RDLOCK(ps);
+ if (ps->immediate_exit && !ps->generating_done) {
+ if (SWIFT_FAILED(sresult = swift_port_stop(port, tts_stream, SWIFT_EVENT_NOW))) {
+ ast_log(LOG_NOTICE, "Early top of swift port failed\n");
+ } else {
+ ast_log(LOG_DEBUG, "Early stop of swift port returned okay\n");
+ }
+ }
+ ASTOBJ_UNLOCK(ps);
+ }
+
+ if (alreadyran == 0 && timeout > 0 && max_digits > 0) {
+ rc = listen_for_dtmf(chan, timeout, max_digits);
+ if (rc) {
+ sprintf(results, "%d", rc);
+ ast_log(LOG_NOTICE, "DTMF = %s\n", results);
+ pbx_builtin_setvar_helper(chan, "SWIFT_DTMF", results);
+ } else {
+ ast_log(LOG_NOTICE, "No DTMF\n");
+ }
+ }
+
+ if (max_digits == 1 && rc) {
+ if (cfg_goto_exten) {
+ if (ast_exists_extension (chan, chan->context, results, 1, chan->cid.cid_num)) {
+ strncpy(chan->exten, tmp_exten, sizeof(chan->exten) - 1);
+ chan->priority = 0;
+ }
+ }
+ }
+
+
+exception:
+ if(port!=NULL) swift_port_close(port);
+ if(engine!=NULL) swift_engine_close(engine);
+
+ if (ps && ps->q) {
+ free(ps->q);
+ ps->q = NULL;
+ }
+ if (ps) {
+ free(ps);
+ ps = NULL;
+ }
+
+ if (!res && old_writeformat)
+ ast_set_write_format(chan, old_writeformat);
+
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+ const char *t = NULL;
+ struct ast_config *cfg;
+
+ // Set defaults
+ cfg_buffer_size = 65535;
+ cfg_goto_exten = 0;
+ strncpy(cfg_voice, "David-8kHz", sizeof(cfg_voice));
+
+ res = ast_register_application(app, engine, synopsis, descrip);
+ cfg = ast_config_load(SWIFT_CONFIG_FILE);
+
+ if (cfg) {
+ if ((t = ast_variable_retrieve(cfg, "general", "buffer_size"))) {
+ cfg_buffer_size = atoi(t);
+ ast_log(LOG_DEBUG, "Config buffer_size is %d\n", cfg_buffer_size);
+ }
+ if ((t = ast_variable_retrieve(cfg, "general", "goto_exten"))) {
+ if (!strcmp(t, "yes"))
+ cfg_goto_exten = 1;
+ else
+ cfg_goto_exten = 0;
+ ast_log(LOG_DEBUG, "Config goto_exten is %d\n", cfg_goto_exten);
+ }
+
+ if ((t = ast_variable_retrieve(cfg, "general", "voice"))) {
+ strncpy(cfg_voice, t, sizeof(cfg_voice));
+ ast_log(LOG_DEBUG, "Config voice is %s\n", cfg_voice);
+ }
+
+ ast_config_destroy(cfg);
+
+ } else {
+ ast_log(LOG_NOTICE, "Failed to load config\n");
+ }
+
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+#define AST_MODULE "app_swift"
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Cepstral Swift TTS Application");
32 swift.conf.sample
@@ -0,0 +1,32 @@
+[general]
+; buffer_size
+; default: 65535
+;
+; Number of bytes of audio data to buffer from the Swift libraries.
+; app_swift will allocate this much buffer space for each concurrent running
+; swift app call.
+;
+; A larger buffer allows the swift lib to generate audio and complete sonner,
+; reducing the amount of time we keep the swift port open (consuming a swift
+; concurrency license).
+; You should tune this based on the lenth of things you're having swift speak,
+; how much memory you can afford to burn.
+;
+; 8192 (8kbytes) is a practical minumum
+; You need 8000 bytes to get a second of buffering
+buffer_size=65535
+
+; goto_exten
+; default: no
+;
+; When we get DTMF while swift is speaking, we set ${SWIFT_DTMF} channel variable.
+; If you would also like the swift application to goto the extension indicated
+; by the user's keypress, set this to yes.
+goto_exten=no
+
+; voice
+; default: David-8kHz
+;
+; Set the voice you want swift to use; If the voice you specify is not found,
+; swift will automatically use the default voice it is configured with.
+voice=David-8kHz
Please sign in to comment.
Something went wrong with that request. Please try again.