2 * Asterisk -- A telephony toolkit for Linux.
4 * Asterisk Gateway Interface
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/module.h>
24 #include <sys/signal.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/logger.h>
28 #include <asterisk/options.h>
29 #include <asterisk/image.h>
30 #include <asterisk/say.h>
31 #include "../asterisk.h"
37 /* Recycle some stuff from the CLI interface */
38 #define fdprintf ast_cli
40 typedef struct agi_command {
41 /* Null terminated list of the words of the command */
42 char *cmda[AST_MAX_CMD_LEN];
43 /* Handler for the command (fd for output, # of arguments, argument list).
44 Returns RESULT_SHOWUSAGE for improper arguments */
45 int (*handler)(struct ast_channel *chan, int fd, int argc, char *argv[]);
46 /* Summary of the command (< 60 characters) */
48 /* Detailed usage information */
52 static char *tdesc = "Asterisk Gateway Interface (AGI)";
54 static char *app = "AGI";
56 static char *synopsis = "Executes an AGI compliant application";
58 static char *descrip =
59 " AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
60 "program on a channel. AGI allows Asterisk to launch external programs\n"
61 "written in any language to control a telephony channel, play audio,\n"
62 "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
63 "and stdout. Returns -1 on hangup or if application requested hangup, or\n"
64 "0 on non-hangup exit.\n";
70 #define TONE_BLOCK_SIZE 200
72 static float loudness = 8192.0;
74 unsigned char linear2ulaw(short sample);
75 static void make_tone_block(unsigned char *data, float f1, int *x);
77 static void make_tone_block(unsigned char *data, float f1, int *x)
82 for(i = 0; i < TONE_BLOCK_SIZE; i++)
84 val = loudness * sin((f1 * 2.0 * M_PI * (*x)++)/8000.0);
85 data[i] = linear2ulaw((int)val);
87 /* wrap back around from 8000 */
88 if (*x >= 8000) *x = 0;
92 static int launch_script(char *script, char *args, int *fds, int *opid)
99 if (script[0] != '/') {
100 snprintf(tmp, sizeof(tmp), "%s/%s", AST_AGI_DIR, script);
104 ast_log(LOG_WARNING, "Unable to create toast pipe: %s\n",strerror(errno));
108 ast_log(LOG_WARNING, "unable to create fromast pipe: %s\n", strerror(errno));
115 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
119 /* Redirect stdin and out */
120 dup2(fromast[0], STDIN_FILENO);
121 dup2(toast[1], STDOUT_FILENO);
122 /* Close everything but stdin/out/error */
123 for (x=STDERR_FILENO + 1;x<1024;x++)
126 execl(script, script, args, NULL);
127 ast_log(LOG_WARNING, "Failed to execute '%s': %s\n", script, strerror(errno));
130 if (option_verbose > 2)
131 ast_verbose(VERBOSE_PREFIX_3 "Launched AGI Script %s\n", script);
134 /* close what we're not using in the parent */
142 static void setup_env(struct ast_channel *chan, char *request, int fd)
144 /* Print initial environment, with agi_request always being the first
146 fdprintf(fd, "agi_request: %s\n", request);
147 fdprintf(fd, "agi_channel: %s\n", chan->name);
148 fdprintf(fd, "agi_language: %s\n", chan->language);
149 fdprintf(fd, "agi_type: %s\n", chan->type);
152 fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "");
153 fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "");
155 /* Context information */
156 fdprintf(fd, "agi_context: %s\n", chan->context);
157 fdprintf(fd, "agi_extension: %s\n", chan->exten);
158 fdprintf(fd, "agi_priority: %d\n", chan->priority);
160 /* End with empty return */
164 static int handle_waitfordigit(struct ast_channel *chan, int fd, int argc, char *argv[])
169 return RESULT_SHOWUSAGE;
170 if (sscanf(argv[3], "%i", &to) != 1)
171 return RESULT_SHOWUSAGE;
172 res = ast_waitfordigit(chan, to);
173 fdprintf(fd, "200 result=%d\n", res);
175 return RESULT_SUCCESS;
177 return RESULT_FAILURE;
180 static int handle_sendtext(struct ast_channel *chan, int fd, int argc, char *argv[])
184 return RESULT_SHOWUSAGE;
185 /* At the moment, the parser (perhaps broken) returns with
186 the last argument PLUS the newline at the end of the input
187 buffer. This probably needs to be fixed, but I wont do that
188 because other stuff may break as a result. The right way
189 would probably be to strip off the trailing newline before
190 parsing, then here, add a newline at the end of the string
191 before sending it to ast_sendtext --DUDE */
192 res = ast_sendtext(chan, argv[2]);
193 fdprintf(fd, "200 result=%d\n", res);
195 return RESULT_SUCCESS;
197 return RESULT_FAILURE;
200 static int handle_recvchar(struct ast_channel *chan, int fd, int argc, char *argv[])
204 return RESULT_SHOWUSAGE;
205 res = ast_recvchar(chan,atoi(argv[2]));
207 fdprintf(fd, "200 result=%d (timeout)\n", res);
208 return RESULT_SUCCESS;
211 fdprintf(fd, "200 result=%d\n", res);
212 return RESULT_SUCCESS;
215 fdprintf(fd, "200 result=%d (hangup)\n", res);
216 return RESULT_FAILURE;
220 static int handle_tddmode(struct ast_channel *chan, int fd, int argc, char *argv[])
224 return RESULT_SHOWUSAGE;
225 if (!strncasecmp(argv[2],"on",2)) x = 1; else x = 0;
226 res = ast_channel_setoption(chan,AST_OPTION_TDD,&x,sizeof(char),0);
227 fdprintf(fd, "200 result=%d\n", res);
229 return RESULT_SUCCESS;
231 return RESULT_FAILURE;
234 static int handle_sendimage(struct ast_channel *chan, int fd, int argc, char *argv[])
238 return RESULT_SHOWUSAGE;
239 res = ast_send_image(chan, argv[2]);
240 if (!ast_check_hangup(chan))
242 fdprintf(fd, "200 result=%d\n", res);
244 return RESULT_SUCCESS;
246 return RESULT_FAILURE;
249 static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *argv[])
253 return RESULT_SHOWUSAGE;
254 res = ast_streamfile(chan, argv[2],chan->language);
256 fdprintf(fd, "200 result=%d\n", res);
258 return RESULT_SHOWUSAGE;
260 return RESULT_FAILURE;
262 res = ast_waitstream(chan, argv[3]);
264 fdprintf(fd, "200 result=%d\n", res);
266 return RESULT_SUCCESS;
268 return RESULT_FAILURE;
271 static int handle_saynumber(struct ast_channel *chan, int fd, int argc, char *argv[])
276 return RESULT_SHOWUSAGE;
277 if (sscanf(argv[2], "%i", &num) != 1)
278 return RESULT_SHOWUSAGE;
279 res = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
280 fdprintf(fd, "200 result=%d\n", res);
282 return RESULT_SUCCESS;
284 return RESULT_FAILURE;
287 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout);
289 static int handle_getdata(struct ast_channel *chan, int fd, int argc, char *argv[])
297 return RESULT_SHOWUSAGE;
298 if (argc >= 4) timeout = atoi(argv[3]); else timeout = 0;
299 if (argc >= 5) max = atoi(argv[4]); else max = 50;
300 res = ast_app_getdata(chan, argv[2], data, max, timeout);
302 fdprintf(fd, "200 result=%s (timeout)\n", data);
304 fdprintf(fd, "200 result=%s\n", data);
306 return RESULT_SUCCESS;
308 return RESULT_FAILURE;
311 static int handle_setcontext(struct ast_channel *chan, int fd, int argc, char *argv[])
315 return RESULT_SHOWUSAGE;
316 strncpy(chan->context, argv[2], sizeof(chan->context)-1);
317 fdprintf(fd, "200 result=0\n");
318 return RESULT_SUCCESS;
321 static int handle_setextension(struct ast_channel *chan, int fd, int argc, char **argv)
324 return RESULT_SHOWUSAGE;
325 strncpy(chan->exten, argv[2], sizeof(chan->exten)-1);
326 fdprintf(fd, "200 result=0\n");
327 return RESULT_SUCCESS;
330 static int handle_setpriority(struct ast_channel *chan, int fd, int argc, char **argv)
334 return RESULT_SHOWUSAGE;
335 if (sscanf(argv[2], "%i", &pri) != 1)
336 return RESULT_SHOWUSAGE;
337 chan->priority = pri - 1;
338 fdprintf(fd, "200 result=0\n");
339 return RESULT_SUCCESS;
342 static int ms_diff(struct timeval *tv1, struct timeval *tv2)
346 ms = (tv1->tv_sec - tv2->tv_sec) * 1000;
347 ms += (tv1->tv_usec - tv2->tv_usec) / 1000;
351 static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *argv[])
353 struct ast_filestream *fs;
354 struct ast_frame *f,wf;
355 struct timeval tv, start, lastout, now, notime = { 0,0 } ;
357 unsigned char tone_block[TONE_BLOCK_SIZE];
362 return RESULT_SHOWUSAGE;
363 if (sscanf(argv[5], "%i", &ms) != 1)
364 return RESULT_SHOWUSAGE;
366 if (argc > 6) { /* if to beep */
368 lastout.tv_sec = lastout.tv_usec = 0;
369 for(j = 0; j < 13; j++)
371 do gettimeofday(&now,NULL);
372 while (lastout.tv_sec &&
373 (ms_diff(&now,&lastout) < 25));
374 lastout.tv_sec = now.tv_sec;
375 lastout.tv_usec = now.tv_usec;
376 wf.frametype = AST_FRAME_VOICE;
377 wf.subclass = AST_FORMAT_ULAW;
378 wf.offset = AST_FRIENDLY_OFFSET;
380 wf.data = tone_block;
381 wf.datalen = TONE_BLOCK_SIZE;
382 /* make this tone block */
383 make_tone_block(tone_block,1000.0,&i);
384 wf.timelen = wf.datalen / 8;
385 if (ast_write(chan, &wf)) {
386 fdprintf(fd, "200 result=%d (hangup)\n", 0);
387 return RESULT_FAILURE;
390 FD_SET(chan->fds[0],&readfds);
391 /* if no read avail, do send again */
392 if (select(chan->fds[0] + 1,&readfds,NULL,
393 NULL,¬ime) < 1) continue;
396 fdprintf(fd, "200 result=%d (hangup)\n", 0);
397 return RESULT_FAILURE;
399 switch(f->frametype) {
401 if (strchr(argv[4], f->subclass)) {
402 /* This is an interrupting chracter */
403 fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
405 return RESULT_SUCCESS;
408 case AST_FRAME_VOICE:
409 break; /* throw it away */
413 /* suck in 5 voice frames to make up for echo of beep, etc */
414 for(i = 0; i < 5; i++) {
417 fdprintf(fd, "200 result=%d (hangup)\n", 0);
418 return RESULT_FAILURE;
420 switch(f->frametype) {
422 if (strchr(argv[4], f->subclass)) {
423 /* This is an interrupting chracter */
424 fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
426 return RESULT_SUCCESS;
429 case AST_FRAME_VOICE:
430 break; /* throw it away */
437 fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_TRUNC | O_WRONLY, 0, 0644);
439 fdprintf(fd, "200 result=%d (writefile)\n", res);
440 return RESULT_FAILURE;
442 gettimeofday(&start, NULL);
443 gettimeofday(&tv, NULL);
444 while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
445 res = ast_waitfor(chan, -1);
448 fdprintf(fd, "200 result=%d (waitfor)\n", res);
449 return RESULT_FAILURE;
453 fdprintf(fd, "200 result=%d (hangup)\n", 0);
455 return RESULT_FAILURE;
457 switch(f->frametype) {
459 if (strchr(argv[4], f->subclass)) {
460 /* This is an interrupting chracter */
461 fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
464 return RESULT_SUCCESS;
467 case AST_FRAME_VOICE:
468 ast_writestream(fs, f);
472 gettimeofday(&tv, NULL);
474 fdprintf(fd, "200 result=%d (timeout)\n", 0);
476 return RESULT_SUCCESS;
479 static char usage_waitfordigit[] =
480 " Usage: WAIT FOR DIGIT <timeout>\n"
481 " Waits up to 'timeout' seconds for channel to receive a DTMF digit.\n"
482 " Returns -1 on channel failure, 0 if no digit is received in the timeout, or\n"
483 " the numerical value of the ascii of the digit if one is received. Use -1\n"
484 " for the timeout value if you desire the call to block indefinitely.\n";
486 static char usage_sendtext[] =
487 " Usage: SEND TEXT \"<text to send>\"\n"
488 " Sends the given text on a channel. Most channels do not support the\n"
489 " transmission of text. Returns 0 if text is sent, or if the channel does not\n"
490 " support text transmission. Returns -1 only on error/hangup. Text\n"
491 " consisting of greater than one word should be placed in quotes since the\n"
492 " command only accepts a single argument.\n";
494 static char usage_recvchar[] =
495 " Usage: RECEIVE CHAR <timeout>\n"
496 " Receives a character of text on a channel. Specify timeout to be the\n"
497 " maximum time to wait for input in milliseconds, or 0 for infinite. Most channels\n"
498 " do not support the reception of text. Returns the decimal value of the character\n"
499 " if one is received, or 0 if the channel does not support text reception. Returns\n"
500 " -1 only on error/hangup.\n";
502 static char usage_tddmode[] =
503 " Usage: TDD MODE <on|off>\n"
504 " Enable/Disable TDD transmission/reception on a channel. Returns 1 if\n"
505 " successful, or 0 if channel is not TDD-capable.\n";
507 static char usage_sendimage[] =
508 " Usage: SEND IMAGE <image>\n"
509 " Sends the given image on a channel. Most channels do not support the\n"
510 " transmission of images. Returns 0 if image is sent, or if the channel does not\n"
511 " support image transmission. Returns -1 only on error/hangup. Image names\n"
512 " should not include extensions.\n";
514 static char usage_streamfile[] =
515 " Usage: STREAM FILE <filename> <escape digits>\n"
516 " Send the given file, allowing playback to be interrupted by the given\n"
517 " digits, if any. Use double quotes for the digits if you wish none to be\n"
518 " permitted. Returns 0 if playback completes without a digit being pressed, or\n"
519 " the ASCII numerical value of the digit if one was pressed, or -1 on error or\n"
520 " if the channel was disconnected. Remember, the file extension must not be\n"
521 " included in the filename.\n";
523 static char usage_saynumber[] =
524 " Usage: SAY NUMBER <number> <escape digits>\n"
525 " Say a given number, returning early if any of the given DTMF digits\n"
526 " are received on the channel. Returns 0 if playback completes without a digit\n"
527 " being pressed, or the ASCII numerical value of the digit if one was pressed or\n"
528 " -1 on error/hangup.\n";
530 static char usage_getdata[] =
531 " Usage: GET DATA <file to be streamed> [timeout] [max digits]\n"
532 " Stream the given file, and recieve DTMF data. Returns the digits recieved\n"
533 "from the channel at the other end.\n";
535 static char usage_setcontext[] =
536 " Usage: SET CONTEXT <desired context>\n"
537 " Sets the context for continuation upon exiting the application.\n";
539 static char usage_setextension[] =
540 " Usage: SET EXTENSION <new extension>\n"
541 " Changes the extension for continuation upon exiting the application.\n";
543 static char usage_setpriority[] =
544 " Usage: SET PRIORITY <num>\n"
545 " Changes the priority for continuation upon exiting the application.\n";
547 static char usage_recordfile[] =
548 " Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [BEEP]\n"
549 " Record to a file until a given dtmf digit in the sequence is received\n"
550 " Returns -1 on hangup or error. The format will specify what kind of file\n"
551 " will be recorded. The timeout is the maximum record time in milliseconds, or\n"
552 " -1 for no timeout\n";
554 agi_command commands[] = {
555 { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
556 { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
557 { { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
558 { { "tdd", "mode", NULL }, handle_tddmode, "Sends text to channels supporting it", usage_tddmode },
559 { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
560 { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
561 { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
562 { { "get", "data", NULL }, handle_getdata, "Gets data on a channel", usage_getdata },
563 { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
564 { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
565 { { "set", "priority", NULL }, handle_setpriority, "Prioritizes the channel", usage_setpriority },
566 { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile }
569 static agi_command *find_command(char *cmds[])
574 for (x=0;x < sizeof(commands) / sizeof(commands[0]);x++) {
575 /* start optimistic */
577 for (y=0;match && cmds[y]; y++) {
578 /* If there are no more words in the command (and we're looking for
579 an exact match) or there is a difference between the two words,
580 then this is not a match */
581 if (!commands[x].cmda[y])
583 if (strcasecmp(commands[x].cmda[y], cmds[y]))
586 /* If more words are needed to complete the command then this is not
587 a candidate (unless we're looking for a really inexact answer */
588 if (commands[x].cmda[y])
597 static int parse_args(char *s, int *max, char *argv[])
609 /* If it's escaped, put a literal quote */
614 if (quoted && whitespace) {
615 /* If we're starting a quote, coming off white space start a new word, too */
623 if (!quoted && !escaped) {
624 /* If we're not quoted, mark this as whitespace, and
625 end the previous argument */
629 /* Otherwise, just treat it as anything else */
633 /* If we're escaped, print a literal, otherwise enable escaping */
643 if (x >= MAX_ARGS -1) {
644 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
647 /* Coming off of whitespace, start the next argument */
663 static int agi_handle_command(struct ast_channel *chan, int fd, char *buf)
665 char *argv[MAX_ARGS];
670 parse_args(buf, &argc, argv);
674 fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
676 c = find_command(argv);
678 res = c->handler(chan, fd, argc, argv);
680 case RESULT_SHOWUSAGE:
681 fdprintf(fd, "520-Invalid command syntax. Proper usage follows:\n");
682 fdprintf(fd, c->usage);
683 fdprintf(fd, "520 End of proper usage.\n");
686 /* They've already given the failure. We've been hung up on so handle this
691 fdprintf(fd, "510 Invalid or unknown command\n");
696 static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid)
698 struct ast_channel *c;
701 int returnstatus = 0;
705 if (!(readf = fdopen(fds[0], "r"))) {
706 ast_log(LOG_WARNING, "Unable to fdopen file descriptor\n");
711 setup_env(chan, request, fds[1]);
714 c = ast_waitfor_nandfds(&chan, 1, &fds[0], 1, NULL, &outfd, &ms);
716 /* Idle the channel until we get a command */
719 ast_log(LOG_DEBUG, "%s hungup\n", chan->name);
725 } else if (outfd > -1) {
726 if (!fgets(buf, sizeof(buf), readf)) {
727 /* Program terminated */
730 if (option_verbose > 2)
731 ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
732 /* No need to kill the pid anymore, since they closed us */
737 /* Un-comment this code to fix the problem with
738 the newline being included in the parsed
739 command string(s) output --DUDE */
740 /* get rid of trailing newline, if any */
741 if (*buf && buf[strlen(buf) - 1] == '\n')
742 buf[strlen(buf) - 1] = 0;
744 returnstatus |= agi_handle_command(chan, fds[1], buf);
745 /* If the handle_command returns -1, we need to stop */
746 if (returnstatus < 0) {
750 ast_log(LOG_WARNING, "No channel, no fd?\n");
762 static int agi_exec(struct ast_channel *chan, void *data)
770 if (!data || !strlen(data)) {
771 ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
776 strncpy(tmp, data, sizeof(tmp)-1);
778 args = strtok(NULL, "|");
779 ringy = strtok(NULL,"|");
783 /* Answer if need be */
784 if (chan->state != AST_STATE_UP) {
785 if (ringy) { /* if for ringing first */
786 /* a little ringy-dingy first */
787 ast_indicate(chan, AST_CONTROL_RINGING);
790 if (ast_answer(chan)) {
791 LOCAL_USER_REMOVE(u);
795 res = launch_script(tmp, args, fds, &pid);
797 res = run_agi(chan, tmp, fds, pid);
801 LOCAL_USER_REMOVE(u);
805 int unload_module(void)
807 STANDARD_HANGUP_LOCALUSERS;
808 return ast_unregister_application(app);
811 int load_module(void)
813 return ast_register_application(app, agi_exec, synopsis, descrip);
816 char *description(void)
824 STANDARD_USECOUNT(res);
830 return ASTERISK_GPL_KEY;
839 static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
840 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
841 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
842 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
843 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
844 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
845 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
846 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
847 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
848 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
849 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
850 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
851 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
852 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
853 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
854 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
855 int sign, exponent, mantissa;
856 unsigned char ulawbyte;
858 /* Get the sample into sign-magnitude. */
859 sign = (sample >> 8) & 0x80; /* set aside the sign */
860 if (sign != 0) sample = -sample; /* get magnitude */
861 if (sample > CLIP) sample = CLIP; /* clip the magnitude */
863 /* Convert from 16 bit linear to ulaw. */
864 sample = sample + BIAS;
865 exponent = exp_lut[(sample >> 7) & 0xFF];
866 mantissa = (sample >> (exponent + 3)) & 0x0F;
867 ulawbyte = ~(sign | (exponent << 4) | mantissa);
869 if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */