--- chan_alsa.c 2004-03-25 17:50:26.000000000 +0100 +++ /tmp/chan_alsa.c 2004-06-15 13:57:40.000000000 +0200 @@ -1,9 +1,13 @@ /* * Asterisk -- A telephony toolkit for Linux. * - * Copyright (C) 2002, Linux Support Services + * This file contains the code for using your soundcard and the ALSA-kernelmodules with Asterisk. * - * By Matthew Fredrickson + * Copyright (C) 2004, Frog Navigation Systems (www.frog.nl) + * + * Base code writen in 2002 by Matthew Fredrickson (Thanks to Linux Support Services) + * + * Updated and bugs fixed in 2004 by Anton Verburg (Thanks to Frog Navigation Systems) * * This program is free software, distributed under the terms of * the GNU General Public License @@ -19,10 +23,7 @@ #include #include #include -#include -#include -#include -#include +#include #include #include #include @@ -36,11 +37,7 @@ #include "ring10.h" #include "answer.h" -#ifdef ALSA_MONITOR -#include "alsa-monitor.h" -#endif -#define DEBUG 0 /* Which device to use */ #define ALSA_INDEV "default" #define ALSA_OUTDEV "default" @@ -50,32 +47,20 @@ #define FRAME_SIZE 160 #define PERIOD_FRAMES 80 /* 80 Frames, at 2 bytes each */ -/* When you set the frame size, you have to come up with - the right buffer format as well. */ -/* 5 64-byte frames = one frame */ -#define BUFFER_FMT ((buffersize * 10) << 16) | (0x0006); - -/* Don't switch between read/write modes faster than every 300 ms */ -#define MIN_SWITCH_TIME 600 - static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; -//static int block = O_NONBLOCK; static char indevname[50] = ALSA_INDEV; static char outdevname[50] = ALSA_OUTDEV; -#if 0 -static struct timeval lasttime; -#endif - static int usecnt; static int needanswer = 0; -static int needringing = 0; -static int needhangup = 0; +static int try_to_connect=0; + +/* XXX ToDo: Silecesuppression and treshold functions should be inplemented... XXX */ +#if 0 static int silencesuppression = 0; static int silencethreshold = 1000; +#endif -static char digits[80] = ""; -static char text2send[80] = ""; static ast_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER; @@ -88,10 +73,6 @@ static char language[MAX_LANGUAGE] = ""; static char exten[AST_MAX_EXTENSION] = "s"; -/* Command pipe */ -static int cmd[2]; - -int hookstate=0; static short silence[FRAME_SIZE] = {0, }; @@ -112,56 +93,36 @@ { AST_CONTROL_ANSWER, answer, sizeof(answer)/2, 2200, 0, 0 }, }; -/* Sound command pipe */ +/* Sound command pipe. Tells the soundthread what to play */ static int sndcmd[2]; +/* File-descriptor for pipe. Because the openend Alsa-card is not written by a filedescriptor, we +should create our own fds. We need it, in order to let channel.c know if a frame is for or from the +soundcard. */ +static int cardfds[2]; + static struct chan_alsa_pvt { /* We only have one ALSA structure -- near sighted perhaps, but it keeps this driver as simple as possible -- as it should be. */ struct ast_channel *owner; char exten[AST_MAX_EXTENSION]; char context[AST_MAX_EXTENSION]; -#if 0 - snd_pcm_t *card; -#endif snd_pcm_t *icard, *ocard; - } alsa; -#if 0 -static int time_has_passed(void) -{ - struct timeval tv; - int ms; - gettimeofday(&tv, NULL); - ms = (tv.tv_sec - lasttime.tv_sec) * 1000 + - (tv.tv_usec - lasttime.tv_usec) / 1000; - if (ms > MIN_SWITCH_TIME) - return -1; - return 0; -} -#endif - -/* Number of buffers... Each is FRAMESIZE/8 ms long. For example - with 160 sample frames, and a buffer size of 3, we have a 60ms buffer, - usually plenty. */ - +/* This soundthread will listen for a 'making call', 'ringing', etc. command */ pthread_t sthread; -#define MAX_BUFFER_SIZE 100 -//static int buffersize = 3; -//static int full_duplex = 0; - -/* Are we reading or writing (simulated full duplex) */ -//static int readmode = 1; - -/* File descriptors for sound device */ +/* File descriptors for polling the sound device */ static int readdev = -1; static int writedev = -1; +/* Use autoanswer by default */ static int autoanswer = 1; +/* XXX Hm, seems to me that alsa has an own function for this task, isn't it? +Well, this should be implemented in future... XXX */ #if 0 static int calc_loudness(short *frame) { @@ -182,9 +143,10 @@ static int sampsent = 0; static int silencelen=0; static int offset=0; -static int nosound=0; -static int send_sound(void) + +/* This function dumps the requested sound at the card... */ +static int put_sound(void) { short myframe[FRAME_SIZE]; int total = FRAME_SIZE; @@ -225,18 +187,13 @@ offset = 0; } else { cursound = -1; - nosound = 0; } - return 0; } } - + if (res == 0 || !frame) { return 0; } -#ifdef ALSA_MONITOR - alsa_monitor_write((char *)frame, res * 2); -#endif state = snd_pcm_state(alsa.ocard); if (state == SND_PCM_STATE_XRUN) { snd_pcm_prepare(alsa.ocard); @@ -244,11 +201,13 @@ res = snd_pcm_writei(alsa.ocard, frame, res); if (res > 0) return 0; - return 0; } + return 0; } + +/* Sound thread: Listen to the sndcmd pipe until an action is needed... */ static void *sound_thread(void *unused) { fd_set rfds; @@ -258,51 +217,18 @@ for(;;) { FD_ZERO(&rfds); FD_ZERO(&wfds); - max = sndcmd[0]; + max = writedev; FD_SET(sndcmd[0], &rfds); if (cursound > -1) { FD_SET(writedev, &wfds); if (writedev > max) max = writedev; } -#ifdef ALSA_MONITOR - if (!alsa.owner) { - FD_SET(readdev, &rfds); - if (readdev > max) - max = readdev; - } -#endif res = ast_select(max + 1, &rfds, &wfds, NULL, NULL); if (res < 1) { ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno)); continue; } -#ifdef ALSA_MONITOR - if (FD_ISSET(readdev, &rfds)) { - /* Keep the pipe going with read audio */ - snd_pcm_state_t state; - short buf[FRAME_SIZE]; - int r; - - state = snd_pcm_state(alsa.ocard); - if (state == SND_PCM_STATE_XRUN) { - snd_pcm_prepare(alsa.ocard); - } - r = snd_pcm_readi(alsa.icard, buf, FRAME_SIZE); - if (r == -EPIPE) { -#if DEBUG - ast_log(LOG_ERROR, "XRUN read\n"); -#endif - snd_pcm_prepare(alsa.icard); - } else if (r == -ESTRPIPE) { - ast_log(LOG_ERROR, "-ESTRPIPE\n"); - snd_pcm_prepare(alsa.icard); - } else if (r < 0) { - ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r)); - } else - alsa_monitor_read((char *)buf, r * 2); - } -#endif if (FD_ISSET(sndcmd[0], &rfds)) { read(sndcmd[0], &cursound, sizeof(cursound)); silencelen = 0; @@ -310,13 +236,14 @@ sampsent = 0; } if (FD_ISSET(writedev, &wfds)) - if (send_sound()) + if (put_sound()) ast_log(LOG_WARNING, "Failed to write sound\n"); } /* Never reached */ return NULL; } +/* Initialize single channel from soundcard */ static snd_pcm_t *alsa_card_init(char *dev, snd_pcm_stream_t stream) { int err; @@ -325,13 +252,10 @@ snd_pcm_hw_params_t *hwparams = NULL; snd_pcm_sw_params_t *swparams = NULL; struct pollfd pfd; - snd_pcm_uframes_t period_size = PERIOD_FRAMES * 4; - //int period_bytes = 0; + snd_pcm_uframes_t period_size = PERIOD_FRAMES * 2; snd_pcm_uframes_t buffer_size = 0; unsigned int rate = DESIRED_RATE; - unsigned int per_min = 1; - //unsigned int per_max = 8; snd_pcm_uframes_t start_threshold, stop_threshold; err = snd_pcm_open(&handle, dev, stream, O_NONBLOCK); @@ -374,7 +298,7 @@ ast_log(LOG_DEBUG, "Period size is %d\n", err); } - buffer_size = 4096 * 2; //period_size * 16; + buffer_size = period_size * 16; err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size); if (err < 0) { ast_log(LOG_WARNING, "Problem setting buffer size of %ld: %s\n", buffer_size, snd_strerror(err)); @@ -403,7 +327,6 @@ snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(handle, swparams); -#if 1 if (stream == SND_PCM_STREAM_PLAYBACK) { start_threshold = period_size; } else { @@ -414,9 +337,7 @@ if (err < 0) { ast_log(LOG_ERROR, "start threshold: %s\n", snd_strerror(err)); } -#endif -#if 1 if (stream == SND_PCM_STREAM_PLAYBACK) { stop_threshold = buffer_size; } else { @@ -426,7 +347,6 @@ if (err < 0) { ast_log(LOG_ERROR, "stop threshold: %s\n", snd_strerror(err)); } -#endif #if 0 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, PERIOD_FRAMES); if (err < 0) { @@ -465,6 +385,7 @@ return handle; } +/* Initalize soundcard */ static int soundcard_init(void) { alsa.icard = alsa_card_init(indevname, SND_PCM_STREAM_CAPTURE); @@ -474,58 +395,65 @@ ast_log(LOG_ERROR, "Problem opening alsa I/O devices\n"); return -1; } - return readdev; } +/* Display digit */ static int alsa_digit(struct ast_channel *c, char digit) { ast_verbose( " << Console Received digit %c >> \n", digit); return 0; } +/*Dislplay string */ static int alsa_text(struct ast_channel *c, char *text) { ast_verbose( " << Console Received text %s >> \n", text); return 0; } +static void answer_sound(void) +{ + int res = 4; + needanswer=1; + try_to_connect=0; + write(sndcmd[1], &res, sizeof(res)); + cursound=-1; + write(cardfds[1], &res, sizeof(res)); + +} + +/* What to do when an incomming call arrives */ static int alsa_call(struct ast_channel *c, char *dest, int timeout) { int res = 3; + struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_RINGING }; ast_verbose( " << Call placed to '%s' on console >> \n", dest); if (autoanswer) { ast_verbose( " << Auto-answered >> \n" ); - needanswer = 1; + answer_sound(); } else { ast_verbose( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n"); - needringing = 1; + ast_queue_frame(c, &f, 0); write(sndcmd[1], &res, sizeof(res)); } return 0; } -static void answer_sound(void) -{ - int res; - nosound = 1; - res = 4; - write(sndcmd[1], &res, sizeof(res)); - -} +/* If autoanswer is disabled, use the command answer, to run this function */ static int alsa_answer(struct ast_channel *c) { ast_verbose( " << Console call has been answered >> \n"); answer_sound(); - ast_setstate(c, AST_STATE_UP); - cursound = -1; return 0; } static int alsa_hangup(struct ast_channel *c) { int res; + if(!try_to_connect) + read(cardfds[0], &res, sizeof(res)); cursound = -1; c->pvt->pvt = NULL; alsa.owner = NULL; @@ -533,51 +461,17 @@ ast_mutex_lock(&usecnt_lock); usecnt--; ast_mutex_unlock(&usecnt_lock); - needhangup = 0; - needanswer = 0; - if (hookstate) { - res = 2; + try_to_connect=0; + needanswer=0; + if(!autoanswer){ + /* Make congestion noice */ + res=2; write(sndcmd[1], &res, sizeof(res)); } return 0; } -#if 0 -static int soundcard_writeframe(short *data) -{ - /* Write an exactly FRAME_SIZE sized of frame */ - static int bufcnt = 0; - static short buffer[FRAME_SIZE * MAX_BUFFER_SIZE * 5]; - struct audio_buf_info info; - int res; - int fd = sounddev; - static int warned=0; - if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)) { - if (!warned) - ast_log(LOG_WARNING, "Error reading output space\n"); - bufcnt = buffersize; - warned++; - } - if ((info.fragments >= buffersize * 5) && (bufcnt == buffersize)) { - /* We've run out of stuff, buffer again */ - bufcnt = 0; - } - if (bufcnt == buffersize) { - /* Write sample immediately */ - res = write(fd, ((void *)data), FRAME_SIZE * 2); - } else { - /* Copy the data into our buffer */ - res = FRAME_SIZE * 2; - memcpy(buffer + (bufcnt * FRAME_SIZE), data, FRAME_SIZE * 2); - bufcnt++; - if (bufcnt == buffersize) { - res = write(fd, ((void *)buffer), FRAME_SIZE * 2 * buffersize); - } - } - return res; -} -#endif - +/* Transport an incomming frame to alsa-routines */ static int alsa_write(struct ast_channel *chan, struct ast_frame *f) { int res; @@ -585,19 +479,14 @@ static int sizpos = 0; int len = sizpos; int pos; - //size_t frames = 0; snd_pcm_state_t state; - /* Immediately return if no sound is enabled */ - if (nosound) - return 0; /* Stop any currently playing sound */ + if (cursound != -1) { snd_pcm_drop(alsa.ocard); snd_pcm_prepare(alsa.ocard); cursound = -1; } - - /* We have to digest the frame in 160-byte portions */ if (f->datalen > sizeof(sizbuf) - sizpos) { ast_log(LOG_WARNING, "Frame too large\n"); @@ -606,18 +495,12 @@ memcpy(sizbuf + sizpos, f->data, f->datalen); len += f->datalen; pos = 0; -#ifdef ALSA_MONITOR - alsa_monitor_write(sizbuf, len); -#endif state = snd_pcm_state(alsa.ocard); if (state == SND_PCM_STATE_XRUN) { snd_pcm_prepare(alsa.ocard); } res = snd_pcm_writei(alsa.ocard, sizbuf, len/2); if (res == -EPIPE) { -#if DEBUG - ast_log(LOG_DEBUG, "XRUN write\n"); -#endif snd_pcm_prepare(alsa.ocard); res = snd_pcm_writei(alsa.ocard, sizbuf, len/2); if (res != len/2) { @@ -632,30 +515,22 @@ ast_log(LOG_ERROR, "You've got some big problems\n"); } } - + res=1; return 0; } - +/* Transport incomming voice from alsaroutines to asterisk frames */ static struct ast_frame *alsa_read(struct ast_channel *chan) { static struct ast_frame f; - static short __buf[FRAME_SIZE + AST_FRIENDLY_OFFSET/2]; + static short __buf[ FRAME_SIZE + AST_FRIENDLY_OFFSET ]; short *buf; static int readpos = 0; - static int left = FRAME_SIZE; - int res; - int b; - int nonull=0; + static int left = FRAME_SIZE; snd_pcm_state_t state; int r = 0; int off = 0; - /* Acknowledge any pending cmd */ - res = read(cmd[0], &b, sizeof(b)); - if (res > 0) - nonull = 1; - f.frametype = AST_FRAME_NULL; f.subclass = 0; f.samples = 0; @@ -664,34 +539,8 @@ f.offset = 0; f.src = type; f.mallocd = 0; - - if (needringing) { - f.frametype = AST_FRAME_CONTROL; - f.subclass = AST_CONTROL_RINGING; - needringing = 0; - return &f; - } - - if (needhangup) { - needhangup = 0; - return NULL; - } - if (strlen(text2send)) { - f.frametype = AST_FRAME_TEXT; - f.subclass = 0; - f.data = text2send; - f.datalen = strlen(text2send); - strcpy(text2send,""); - return &f; - } - if (strlen(digits)) { - f.frametype = AST_FRAME_DTMF; - f.subclass = digits[0]; - for (res=0;res= 0) { off -= r; } @@ -746,21 +591,11 @@ f.offset = AST_FRIENDLY_OFFSET; f.src = type; f.mallocd = 0; -#ifdef ALSA_MONITOR - alsa_monitor_read((char *)buf, FRAME_SIZE * 2); -#endif - -#if 0 - { static int fd = -1; - if (fd < 0) - fd = open("output.raw", O_RDWR | O_TRUNC | O_CREAT); - write(fd, f.data, f.datalen); - } -#endif } return &f; } +/* In case something went wrong */ static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct chan_alsa_pvt *p = newchan->pvt->pvt; @@ -768,6 +603,7 @@ return 0; } +/* What do I have to do for incomming frames? */ static int alsa_indicate(struct ast_channel *chan, int cond) { int res; @@ -791,6 +627,7 @@ return 0; } +/* Load new channel */ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state) { struct ast_channel *tmp; @@ -798,8 +635,7 @@ if (tmp) { snprintf(tmp->name, sizeof(tmp->name), "ALSA/%s", indevname); tmp->type = type; - tmp->fds[0] = readdev; - tmp->fds[1] = cmd[0]; + tmp->fds[0] = cardfds[0]; tmp->nativeformats = AST_FORMAT_SLINEAR; tmp->pvt->pvt = p; tmp->pvt->send_digit = alsa_digit; @@ -834,6 +670,7 @@ return tmp; } +/* So you wanna get a channel? */ static struct ast_channel *alsa_request(char *type, int format, void *data) { int oldformat = format; @@ -854,6 +691,7 @@ return tmp; } +/* Change autoanswer value while in console */ static int console_autoanswer(int fd, int argc, char *argv[]) { if ((argc != 1) && (argc != 2)) @@ -872,6 +710,7 @@ return RESULT_SUCCESS; } +/* Convert on/off to 1/0 */ static char *autoanswer_complete(char *line, char *word, int pos, int state) { #ifndef MIN @@ -890,23 +729,26 @@ return NULL; } +/* Help message in console */ static char autoanswer_usage[] = "Usage: autoanswer [on|off]\n" " Enables or disables autoanswer feature. If used without\n" " argument, displays the current on/off status of autoanswer.\n" " The default value of autoanswer is in 'alsa.conf'.\n"; +/* Manual answer */ static int console_answer(int fd, int argc, char *argv[]) { + struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; if (argc != 1) return RESULT_SHOWUSAGE; if (!alsa.owner) { ast_cli(fd, "No one is calling us\n"); return RESULT_FAILURE; } - hookstate = 1; cursound = -1; - needanswer++; + needanswer = 1; + ast_queue_frame(alsa.owner, &f, 0); answer_sound(); return RESULT_SUCCESS; } @@ -918,6 +760,8 @@ static int console_sendtext(int fd, int argc, char *argv[]) { int tmparg = 2; + char text2send[256]; + struct ast_frame f = { 0, }; if (argc < 2) return RESULT_SHOWUSAGE; if (!alsa.owner) { @@ -927,11 +771,17 @@ if (strlen(text2send)) ast_cli(fd, "Warning: message already waiting to be sent, overwriting\n"); strcpy(text2send, ""); - while(tmparg <= argc) { + while(tmparg < argc) { strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send)); strncat(text2send, " ", sizeof(text2send) - strlen(text2send)); } - needanswer++; + if (strlen(text2send)) { + f.frametype = AST_FRAME_TEXT; + f.subclass = 0; + f.data = text2send; + f.datalen = strlen(text2send); + ast_queue_frame(alsa.owner, &f, 0); + } return RESULT_SUCCESS; } @@ -944,13 +794,12 @@ if (argc != 1) return RESULT_SHOWUSAGE; cursound = -1; - if (!alsa.owner && !hookstate) { + if (!alsa.owner && !try_to_connect) { ast_cli(fd, "No call to hangup up\n"); return RESULT_FAILURE; } - hookstate = 0; if (alsa.owner) { - ast_queue_hangup(alsa.owner, 1); + ast_queue_hangup(alsa.owner, 0); } return RESULT_SUCCESS; } @@ -964,14 +813,16 @@ { char tmp[256], *tmp2; char *mye, *myc; - int b = 0; + int x; + struct ast_frame f = { AST_FRAME_DTMF, 0 }; if ((argc != 1) && (argc != 2)) return RESULT_SHOWUSAGE; if (alsa.owner) { if (argc == 2) { - strncat(digits, argv[1], sizeof(digits) - strlen(digits)); - /* Wake up the polling thread */ - write(cmd[1], &b, sizeof(b)); + for (x=0;x 1) { - ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n"); - ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n"); - } + if ( (res < 0) && (option_verbose > 1) ) { + ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n"); + ast_verbose(VERBOSE_PREFIX_2 "Turn off ALSA support by adding 'noload=chan_alsa.so' in /etc/asterisk/modules.conf\n"); return 0; } -#if 0 - if (!full_duplex) - ast_log(LOG_WARNING, "XXX I don't work right with non-full duplex sound cards XXX\n"); -#endif + + res = ast_channel_register(type, tdesc, AST_FORMAT_SLINEAR, alsa_request); if (res < 0) { ast_log(LOG_ERROR, "Unable to register channel class '%s'\n", type); @@ -1057,10 +899,13 @@ while(v) { if (!strcasecmp(v->name, "autoanswer")) autoanswer = ast_true(v->value); +/* XXX This should be implemented ! XXX */ +#if 0 else if (!strcasecmp(v->name, "silencesuppression")) silencesuppression = ast_true(v->value); else if (!strcasecmp(v->name, "silencethreshold")) silencethreshold = atoi(v->value); +#endif else if (!strcasecmp(v->name, "context")) strncpy(context, v->value, sizeof(context)-1); else if (!strcasecmp(v->name, "language")) @@ -1076,11 +921,6 @@ ast_destroy(cfg); } pthread_create(&sthread, NULL, sound_thread, NULL); -#ifdef ALSA_MONITOR - if (alsa_monitor_start()) { - ast_log(LOG_ERROR, "Problem starting Monitoring\n"); - } -#endif return 0; } @@ -1091,19 +931,19 @@ int x; for (x=0;x 0) { - close(cmd[0]); - close(cmd[1]); + if (cardfds[0] > 0) { + close(sndcmd[0]); + close(sndcmd[1]); } if (sndcmd[0] > 0) { close(sndcmd[0]); close(sndcmd[1]); } - if (alsa.owner) - ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD); - if (alsa.owner) + if (alsa.owner) return -1; return 0; }