Revert earlier jcdutton ALSA improvements which are not disclaimed
authorMark Spencer <markster@digium.com>
Sun, 11 Apr 2004 01:59:11 +0000 (01:59 +0000)
committerMark Spencer <markster@digium.com>
Sun, 11 Apr 2004 01:59:11 +0000 (01:59 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@2674 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_alsa.c

index 57d0fdb..8446a7a 100755 (executable)
 #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 DESIRED_RATE 8000
 
 /* Lets use 160 sample frames, just like GSM.  */
-#define PERIOD_SIZE 160
-#define ALSA_MAX_BUF PERIOD_SIZE*4 + AST_FRIENDLY_OFFSET
+#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;
 
-static int usecnt;
-static int silencesuppression = 0;
-static int silencethreshold = 1000;
-
 #if 0
 static struct timeval lasttime;
 #endif
 
+static int usecnt;
+static int needanswer = 0;
+static int needringing = 0;
+static int needhangup = 0;
+static int silencesuppression = 0;
+static int silencethreshold = 1000;
+
 static char digits[80] = "";
 static char text2send[80] = "";
 
@@ -77,7 +93,7 @@ static int cmd[2];
 
 int hookstate=0;
 
-static short silence[PERIOD_SIZE] = {0, };
+static short silence[FRAME_SIZE] = {0, };
 
 struct sound {
        int ind;
@@ -99,221 +115,223 @@ static struct sound sounds[] = {
 /* Sound command pipe */
 static int sndcmd[2];
 
-typedef struct chan_alsa_pvt chan_alsa_pvt_t;
-struct chan_alsa_pvt {
+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];
-       struct pollfd                *pfd;
-       unsigned int  playback_nfds;
-       unsigned int  capture_nfds;
-       snd_pcm_t *playback_handle;
-       snd_pcm_t *capture_handle;
-       snd_pcm_uframes_t capture_period_size;
-        snd_pcm_uframes_t capture_buffer_size;
+#if 0
+       snd_pcm_t *card;
+#endif
+       snd_pcm_t *icard, *ocard;
        
-       pthread_t sound_thread;
-       char buf[ALSA_MAX_BUF];          /* buffer for reading frames */
-       char *capture_buf;             /* malloc buffer for reading frames */
-       struct ast_frame fr;
-       int cursound;
-       int cursound_offset;
-       int nosound;
-};
+} 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. */
 
-static chan_alsa_pvt_t alsa;
+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 */
+static int readdev = -1;
+static int writedev = -1;
 
 static int autoanswer = 1;
 
-/* Send a announcement */
-static int send_sound(chan_alsa_pvt_t *driver)
+#if 0 
+static int calc_loudness(short *frame)
+{
+       int sum = 0;
+       int x;
+       for (x=0;x<FRAME_SIZE;x++) {
+               if (frame[x] < 0)
+                       sum -= frame[x];
+               else
+                       sum += frame[x];
+       }
+       sum = sum/FRAME_SIZE;
+       return sum;
+}
+#endif
+
+static int cursound = -1;
+static int sampsent = 0;
+static int silencelen=0;
+static int offset=0;
+static int nosound=0;
+
+static int send_sound(void)
 {
+       short myframe[FRAME_SIZE];
+       int total = FRAME_SIZE;
+       short *frame = NULL;
+       int amt=0;
        int res;
-       int frames;
-       int cursound=driver->cursound;
+       int myoff;
        snd_pcm_state_t state;
 
        if (cursound > -1) {
-               driver->nosound=1;
-               state = snd_pcm_state(alsa.playback_handle);
-               if (state == SND_PCM_STATE_XRUN) {
-                       snd_pcm_prepare(alsa.playback_handle);
-               }
-               frames = sounds[cursound].samplen - driver->cursound_offset;
-               if (frames >= PERIOD_SIZE)  {
-                       res = snd_pcm_writei(driver->playback_handle,sounds[cursound].data + (driver->cursound_offset*2), PERIOD_SIZE);
-                       driver->cursound_offset+=PERIOD_SIZE;
-               } else if (frames > 0) {
-                       res = snd_pcm_writei(driver->playback_handle,sounds[cursound].data + (driver->cursound_offset*2), frames);
-                       res = snd_pcm_writei(driver->playback_handle,silence, PERIOD_SIZE - frames);
-                       driver->cursound_offset+=PERIOD_SIZE;
+               res = total;
+               if (sampsent < sounds[cursound].samplen) {
+                       myoff=0;
+                       while(total) {
+                               amt = total;
+                               if (amt > (sounds[cursound].datalen - offset)) 
+                                       amt = sounds[cursound].datalen - offset;
+                               memcpy(myframe + myoff, sounds[cursound].data + offset, amt * 2);
+                               total -= amt;
+                               offset += amt;
+                               sampsent += amt;
+                               myoff += amt;
+                               if (offset >= sounds[cursound].datalen)
+                                       offset = 0;
+                       }
+                       /* Set it up for silence */
+                       if (sampsent >= sounds[cursound].samplen) 
+                               silencelen = sounds[cursound].silencelen;
+                       frame = myframe;
+               } else {
+                       if (silencelen > 0) {
+                               frame = silence;
+                               silencelen -= res;
                        } else {
-                       res = snd_pcm_writei(driver->playback_handle,silence, PERIOD_SIZE);
-                       driver->cursound_offset+=PERIOD_SIZE;
-               }
-               if (driver->cursound_offset > ( sounds[cursound].samplen + sounds[cursound].silencelen ) ) {
                                if (sounds[cursound].repeat) {
-                               driver->cursound_offset=0;
+                                       /* Start over */
+                                       sampsent = 0;
+                                       offset = 0;
                                } else {
-                               driver->cursound = -1;
-                               driver->nosound=0;
+                                       cursound = -1;
+                                       nosound = 0;
                                }
+                       return 0;
                        }
                }
+               
+               if (res == 0 || !frame) {
                        return 0;
-}
-
-static int sound_capture(chan_alsa_pvt_t *driver)
-{
-       struct ast_frame *fr = &driver->fr;
-       char *readbuf = ((char *)driver->buf) + AST_FRIENDLY_OFFSET;
-       snd_pcm_sframes_t err;
-       snd_pcm_sframes_t avail;
-       snd_pcm_state_t alsa_state;
-       
-       /* Update positions */
-       while ((avail = snd_pcm_avail_update (driver->capture_handle)) >= PERIOD_SIZE) {
-       
-               /* capture samples from sound card */
-               err = snd_pcm_readi(driver->capture_handle, readbuf, PERIOD_SIZE);
-               if (err == -EPIPE) {
-                       ast_log(LOG_ERROR, "XRUN read avail=%ld\n", avail);
-                       snd_pcm_prepare(driver->capture_handle);
-                       alsa_state = snd_pcm_state(driver->capture_handle);
-                       if (alsa_state == SND_PCM_STATE_PREPARED) {
-                               snd_pcm_start(driver->capture_handle);
                }
-                       continue;
-               } else if (err == -ESTRPIPE) {
-                       ast_log(LOG_ERROR, "-ESTRPIPE\n");
-                       snd_pcm_prepare(driver->capture_handle);
-                       alsa_state = snd_pcm_state(driver->capture_handle);
-                       if (alsa_state == SND_PCM_STATE_PREPARED) {
-                               snd_pcm_start(driver->capture_handle);
+#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);
                }
-                       continue;
-               } else if (err < 0) {
-                       ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(err));
-                       return -1;
-       }
-
-               /* Now send captures samples */
-               fr->frametype = AST_FRAME_VOICE;
-               fr->src = type;
-               fr->mallocd = 0;
-
-               fr->subclass = AST_FORMAT_SLINEAR;
-               fr->samples = PERIOD_SIZE;
-               fr->datalen = PERIOD_SIZE * 2 ; /* 16bit = X * 2 */
-               fr->data = readbuf;
-               fr->offset = AST_FRIENDLY_OFFSET;
-
-               if (driver->owner) ast_queue_frame(driver->owner, fr);
+               res = snd_pcm_writei(alsa.ocard, frame, res);
+               if (res > 0)
+                       return 0;
+               return 0;
        }
-       return 0; /* 0 = OK, !=0 -> Error */
+       return 0;
 }
 
-static void *sound_thread(void *pvt)
+static void *sound_thread(void *unused)
 {
-       chan_alsa_pvt_t *driver = (chan_alsa_pvt_t *)pvt;
-        unsigned int nfds;
-        unsigned int ci;
-       unsigned short revents;
-       snd_pcm_state_t alsa_state;
+       fd_set rfds;
+       fd_set wfds;
+       int max;
        int res;
-       if (driver->playback_handle) {
-                driver->playback_nfds =
-                        snd_pcm_poll_descriptors_count (
-                                driver->playback_handle);
-        } else {
-                driver->playback_nfds = 0;
+       for(;;) {
+               FD_ZERO(&rfds);
+               FD_ZERO(&wfds);
+               max = sndcmd[0];
+               FD_SET(sndcmd[0], &rfds);
+               if (cursound > -1) {
+                       FD_SET(writedev, &wfds);
+                       if (writedev > max)
+                               max = writedev;
                }
-
-        if (driver->capture_handle) {
-                driver->capture_nfds =
-                        snd_pcm_poll_descriptors_count (driver->capture_handle);
-        } else {
-                driver->capture_nfds = 0;
+#ifdef ALSA_MONITOR
+               if (!alsa.owner) {
+                       FD_SET(readdev, &rfds);
+                       if (readdev > max)
+                               max = readdev;
                }
-
-        if (driver->pfd) {
-                free (driver->pfd);
+#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;
                        
-        driver->pfd = (struct pollfd *)
-                malloc (sizeof (struct pollfd) *
-                        (driver->playback_nfds + driver->capture_nfds + 2));
-
-        nfds = 0;
-        if (driver->playback_handle) {
-               snd_pcm_poll_descriptors (driver->playback_handle,
-                                          &driver->pfd[0],
-                                          driver->playback_nfds);
-                nfds += driver->playback_nfds;
+                       state = snd_pcm_state(alsa.ocard);
+                       if (state == SND_PCM_STATE_XRUN) {
+                               snd_pcm_prepare(alsa.ocard);
                        }
-        ci = nfds;
-
-        if (driver->capture_handle) {
-                snd_pcm_poll_descriptors (driver->capture_handle,
-                                          &driver->pfd[ci],
-                                          driver->capture_nfds);
-                nfds += driver->capture_nfds;
+                       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);
                }               
-       
-       while (hookstate) {
-               /* When no doing announcements */
-               if (driver->cursound > -1) {
-                       res = poll(&driver->pfd[0], driver->playback_nfds, -1);
-               } else {
-                       res = poll(&driver->pfd[ci], driver->capture_nfds, -1);
+#endif         
+               if (FD_ISSET(sndcmd[0], &rfds)) {
+                       read(sndcmd[0], &cursound, sizeof(cursound));
+                       silencelen = 0;
+                       offset = 0;
+                       sampsent = 0;
                }
-
-               /* When doing announcements */
-               if (driver->cursound > -1) {
-                       snd_pcm_poll_descriptors_revents(driver->playback_handle, &driver->pfd[0], driver->playback_nfds, &revents);
-                       if (revents & POLLOUT) {
-                               if (send_sound(driver)) {
+               if (FD_ISSET(writedev, &wfds))
+                       if (send_sound())
                                ast_log(LOG_WARNING, "Failed to write sound\n");
        }
-                       }
-               } else {
-               snd_pcm_poll_descriptors_revents(driver->capture_handle, &driver->pfd[ci], driver->capture_nfds, &revents);
-               if (revents & POLLERR) {
-                       alsa_state = snd_pcm_state(driver->capture_handle);
-                       if (alsa_state == SND_PCM_STATE_XRUN) {
-                               snd_pcm_prepare(driver->capture_handle);
-                       }
-                       alsa_state = snd_pcm_state(driver->capture_handle);
-                       if (alsa_state == SND_PCM_STATE_PREPARED) {
-                               snd_pcm_start(driver->capture_handle);
-                       }
-               }
-               if (revents & POLLIN) {
-                       if (sound_capture(driver)) {
-                               ast_log(LOG_WARNING, "Failed to read sound\n");
-                       }
-               }
-               }
-       }
        /* Never reached */
        return NULL;
 }
 
-static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_stream_t stream)
+static snd_pcm_t *alsa_card_init(char *dev, snd_pcm_stream_t stream)
 {
        int err;
        int direction;
        snd_pcm_t *handle = NULL;
        snd_pcm_hw_params_t *hwparams = NULL;
        snd_pcm_sw_params_t *swparams = NULL;
-       snd_pcm_uframes_t period_size = PERIOD_SIZE;
+       struct pollfd pfd;
+       snd_pcm_uframes_t period_size = PERIOD_FRAMES * 4;
+       //int period_bytes = 0;
        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);
@@ -344,13 +362,11 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
 
        direction = 0;
        err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, &direction);
-       
        if (rate != DESIRED_RATE) {
                ast_log(LOG_WARNING, "Rate not correct, requested %d, got %d\n", DESIRED_RATE, rate);
        }
 
        direction = 0;
-       buffer_size = 4096 * 2; /* period_size * 16; */
        err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &direction);
        if (err < 0) {
                ast_log(LOG_ERROR, "period_size(%ld frames) is bad: %s\n", period_size, snd_strerror(err));
@@ -358,7 +374,8 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
                ast_log(LOG_DEBUG, "Period size is %d\n", err);
        }
 
-       err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);   
+       buffer_size = 4096 * 2; //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));
        } else {
@@ -378,15 +395,9 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
        }
 #endif
 
-       if (stream == SND_PCM_STREAM_CAPTURE) {
-               driver->capture_period_size=period_size;
-               driver->capture_buffer_size=buffer_size;
-       }
-
        err = snd_pcm_hw_params(handle, hwparams);
        if (err < 0) {
                ast_log(LOG_ERROR, "Couldn't set the new hw params: %s\n", snd_strerror(err));
-               return NULL;
        }
 
        snd_pcm_sw_params_alloca(&swparams);
@@ -394,7 +405,7 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
 
 #if 1
        if (stream == SND_PCM_STREAM_PLAYBACK) {
-               start_threshold = period_size*3;
+               start_threshold = period_size;
        } else {
                start_threshold = 1;
        }
@@ -409,7 +420,7 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
        if (stream == SND_PCM_STREAM_PLAYBACK) {
                stop_threshold = buffer_size;
        } else {
-               stop_threshold = buffer_size+1;
+               stop_threshold = buffer_size;
        }
        err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
        if (err < 0) {
@@ -417,7 +428,7 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
        }
 #endif
 #if 0
-       err = snd_pcm_sw_params_set_xfer_align(handle, swparams, PERIOD_SIZE);
+       err = snd_pcm_sw_params_set_xfer_align(handle, swparams, PERIOD_FRAMES);
        if (err < 0) {
                ast_log(LOG_ERROR, "Unable to set xfer alignment: %s\n", snd_strerror(err));
        }
@@ -443,25 +454,28 @@ static snd_pcm_t *alsa_card_init(chan_alsa_pvt_t *driver, char *dev, snd_pcm_str
                ast_log(LOG_DEBUG, "Can't handle more than one device\n");
        }
 
+       snd_pcm_poll_descriptors(handle, &pfd, err);
+       ast_log(LOG_DEBUG, "Acquired fd %d from the poll descriptor\n", pfd.fd);
+
+       if (stream == SND_PCM_STREAM_CAPTURE)
+               readdev = pfd.fd;
+       else
+               writedev = pfd.fd;
+
        return handle;
 }
 
 static int soundcard_init(void)
 {
-       alsa.capture_handle = alsa_card_init(&alsa, indevname, SND_PCM_STREAM_CAPTURE);
-       alsa.playback_handle = alsa_card_init(&alsa, outdevname, SND_PCM_STREAM_PLAYBACK);
-       if (!alsa.capture_buf) alsa.capture_buf=malloc(alsa.capture_buffer_size * 2);
+       alsa.icard = alsa_card_init(indevname, SND_PCM_STREAM_CAPTURE);
+       alsa.ocard = alsa_card_init(outdevname, SND_PCM_STREAM_PLAYBACK);
 
-       if (!alsa.capture_handle || !alsa.playback_handle) {
+       if (!alsa.icard || !alsa.ocard) {
                ast_log(LOG_ERROR, "Problem opening alsa I/O devices\n");
-               if (alsa.capture_buf) {
-                       free (alsa.capture_buf);
-                       alsa.capture_buf=0;
-               }
                return -1;
        }
 
-       return 0; /* Success */
+       return readdev;
 }
 
 static int alsa_digit(struct ast_channel *c, char digit)
@@ -478,96 +492,135 @@ static int alsa_text(struct ast_channel *c, char *text)
 
 static int alsa_call(struct ast_channel *c, char *dest, int timeout)
 {
-       chan_alsa_pvt_t *driver = (chan_alsa_pvt_t *)c->pvt->pvt;
        int res = 3;
-        struct ast_frame f = { 0, };
        ast_verbose( " << Call placed to '%s' on console >> \n", dest);
        if (autoanswer) {
                ast_verbose( " << Auto-answered >> \n" );
-                f.frametype = AST_FRAME_CONTROL;
-                f.subclass = AST_CONTROL_ANSWER;
-                ast_queue_frame(c, &f);
+               needanswer = 1;
        } else {
-                driver->nosound = 1;
                ast_verbose( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
-                f.frametype = AST_FRAME_CONTROL;
-                f.subclass = AST_CONTROL_RINGING;
-                ast_queue_frame(c, &f);
-               driver->cursound = res;
+               needringing = 1;
+               write(sndcmd[1], &res, sizeof(res));
        }
        return 0;
 }
 
-static void answer_sound(chan_alsa_pvt_t *driver)
+static void answer_sound(void)
 {
        int res;
-       driver->nosound = 1;
-       driver->cursound = 4;
-       driver->cursound_offset = 0;
+       nosound = 1;
+       res = 4;
+       write(sndcmd[1], &res, sizeof(res));
        
 }
 
 static int alsa_answer(struct ast_channel *c)
 {
-       chan_alsa_pvt_t *driver = (chan_alsa_pvt_t *)c->pvt->pvt;
        ast_verbose( " << Console call has been answered >> \n");
-       answer_sound(driver);
+       answer_sound();
        ast_setstate(c, AST_STATE_UP);
+       cursound = -1;
        return 0;
 }
 
-/* The new_channel is now freed. */
 static int alsa_hangup(struct ast_channel *c)
 {
        int res;
-       chan_alsa_pvt_t *driver = (chan_alsa_pvt_t *)c->pvt->pvt;
-       
-       driver->cursound = -1;
-       driver->nosound = 0;
-        if (hookstate) {
-                hookstate = 0;
-        }
-       pthread_join(driver->sound_thread, NULL);
-/*     snd_pcm_drain(driver->capture_handle); */
-       driver->owner = NULL;
+       cursound = -1;
        c->pvt->pvt = NULL;
+       alsa.owner = NULL;
        ast_verbose( " << Hangup on console >> \n");
        ast_mutex_lock(&usecnt_lock);
        usecnt--;
        ast_mutex_unlock(&usecnt_lock);
+       needhangup = 0;
+       needanswer = 0;
+       if (hookstate) {
+               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
+
 static int alsa_write(struct ast_channel *chan, struct ast_frame *f)
 {
-       chan_alsa_pvt_t *driver = (chan_alsa_pvt_t *)chan->pvt->pvt;
        int res;
        static char sizbuf[8000];
        static int sizpos = 0;
        int len = sizpos;
        int pos;
+       //size_t frames = 0;
        snd_pcm_state_t state;
-       snd_pcm_sframes_t delay = 0;
-
-       if (driver->nosound) {
+       /* 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;
        }
-       state = snd_pcm_state(driver->playback_handle);
-       if (state == SND_PCM_STATE_XRUN) {
-               snd_pcm_prepare(driver->playback_handle);
+       
+
+       /* We have to digest the frame in 160-byte portions */
+       if (f->datalen > sizeof(sizbuf) - sizpos) {
+               ast_log(LOG_WARNING, "Frame too large\n");
+               return -1;
        }
-       res = snd_pcm_delay( driver->playback_handle, &delay );
-       if (delay > 4 * PERIOD_SIZE) {
-               return 0;
+       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(driver->playback_handle, f->data, f->samples);
+       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(driver->playback_handle);
-               res = snd_pcm_writei(driver->playback_handle, f->data, f->samples);
-               if (res != f->samples) {
+               snd_pcm_prepare(alsa.ocard);
+               res = snd_pcm_writei(alsa.ocard, sizbuf, len/2);
+               if (res != len/2) {
                        ast_log(LOG_ERROR, "Write error: %s\n", snd_strerror(res));
                        return -1;
                } else if (res < 0) {
@@ -587,26 +640,21 @@ static int alsa_write(struct ast_channel *chan, struct ast_frame *f)
 static struct ast_frame *alsa_read(struct ast_channel *chan)
 {
        static struct ast_frame f;
-       static short __buf[PERIOD_SIZE + AST_FRIENDLY_OFFSET/2];
+       static short __buf[FRAME_SIZE + AST_FRIENDLY_OFFSET/2];
        short *buf;
        static int readpos = 0;
-       static int left = PERIOD_SIZE;
+       static int left = FRAME_SIZE;
        int res;
        int b;
        int nonull=0;
        snd_pcm_state_t state;
        int r = 0;
        int off = 0;
-       /* FIXME: This should never been called */
-        ast_log(LOG_WARNING, "ALSA_READ!!!!!\n");
-       return NULL;
-}
-#if 0
+
        /* Acknowledge any pending cmd */
        res = read(cmd[0], &b, sizeof(b));
        if (res > 0)
                nonull = 1;
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        
        f.frametype = AST_FRAME_NULL;
        f.subclass = 0;
@@ -623,13 +671,11 @@ static struct ast_frame *alsa_read(struct ast_channel *chan)
                needringing = 0;
                return &f;
        }
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        
        if (needhangup) {
                needhangup = 0;
                return NULL;
        }
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        if (strlen(text2send)) {
                f.frametype = AST_FRAME_TEXT;
                f.subclass = 0;
@@ -638,7 +684,6 @@ static struct ast_frame *alsa_read(struct ast_channel *chan)
                strcpy(text2send,"");
                return &f;
        }
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        if (strlen(digits)) {
                f.frametype = AST_FRAME_DTMF;
                f.subclass = digits[0];
@@ -647,7 +692,6 @@ static struct ast_frame *alsa_read(struct ast_channel *chan)
                return &f;
        }
        
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        if (needanswer) {
                needanswer = 0;
                f.frametype = AST_FRAME_CONTROL;
@@ -656,29 +700,26 @@ static struct ast_frame *alsa_read(struct ast_channel *chan)
                return &f;
        }
        
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        if (nonull)
                return &f;
                
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
        
-       state = snd_pcm_state(alsa.playback_handle);
+       state = snd_pcm_state(alsa.ocard);
        if (state == SND_PCM_STATE_XRUN) {
-               snd_pcm_prepare(alsa.playback_handle);
+               snd_pcm_prepare(alsa.ocard);
        }
-        ast_log(LOG_WARNING, "alsa: %s:%d\n", __FUNCTION__, __LINE__);
 
        buf = __buf + AST_FRIENDLY_OFFSET/2;
 
-       r = snd_pcm_readi(alsa.capture_handle, buf + readpos, left);
+       r = snd_pcm_readi(alsa.icard, buf + readpos, left);
        if (r == -EPIPE) {
 #if DEBUG
                ast_log(LOG_ERROR, "XRUN read\n");
 #endif
-               snd_pcm_prepare(alsa.capture_handle);
+               snd_pcm_prepare(alsa.icard);
        } else if (r == -ESTRPIPE) {
                ast_log(LOG_ERROR, "-ESTRPIPE\n");
-               snd_pcm_prepare(alsa.capture_handle);
+               snd_pcm_prepare(alsa.icard);
        } else if (r < 0) {
                ast_log(LOG_ERROR, "Read error: %s\n", snd_strerror(r));
                return NULL;
@@ -689,22 +730,25 @@ static struct ast_frame *alsa_read(struct ast_channel *chan)
        readpos += r;
        left -= r;
 
-       if (readpos >= PERIOD_SIZE) {
+       if (readpos >= FRAME_SIZE) {
                /* A real frame */
                readpos = 0;
-               left = PERIOD_SIZE;
+               left = FRAME_SIZE;
                if (chan->_state != AST_STATE_UP) {
                        /* Don't transmit unless it's up */
                        return &f;
                }
                f.frametype = AST_FRAME_VOICE;
                f.subclass = AST_FORMAT_SLINEAR;
-               f.samples = PERIOD_SIZE;
-               f.datalen = PERIOD_SIZE * 2;
+               f.samples = FRAME_SIZE;
+               f.datalen = FRAME_SIZE * 2;
                f.data = buf;
                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;
@@ -716,7 +760,6 @@ static struct ast_frame *alsa_read(struct ast_channel *chan)
        }
        return &f;
 }
-#endif
 
 static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
@@ -727,7 +770,6 @@ static int alsa_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 
 static int alsa_indicate(struct ast_channel *chan, int cond)
 {
-       chan_alsa_pvt_t *driver = (chan_alsa_pvt_t *)chan->pvt->pvt;
        int res;
        switch(cond) {
        case AST_CONTROL_BUSY:
@@ -744,25 +786,20 @@ static int alsa_indicate(struct ast_channel *chan, int cond)
                return -1;
        }
        if (res > -1) {
-               driver->cursound = res;
-               driver->cursound_offset = 0;
-               driver->nosound = 1;
+               write(sndcmd[1], &res, sizeof(res));
        }
        return 0;       
 }
 
-/* New channel is about to be used */
 static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
 {
        struct ast_channel *tmp;
-       snd_pcm_state_t alsa_state;
-       if (!p->capture_handle || !p->playback_handle) {
-               return 0;
-       }
        tmp = ast_channel_alloc(1);
        if (tmp) {
                snprintf(tmp->name, sizeof(tmp->name), "ALSA/%s", indevname);
                tmp->type = type;
+               tmp->fds[0] = readdev;
+               tmp->fds[1] = cmd[0];
                tmp->nativeformats = AST_FORMAT_SLINEAR;
                tmp->pvt->pvt = p;
                tmp->pvt->send_digit = alsa_digit;
@@ -781,7 +818,6 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
                if (strlen(language))
                        strncpy(tmp->language, language, sizeof(tmp->language)-1);
                p->owner = tmp;
-               p->pfd = NULL;
                ast_setstate(tmp, state);
                ast_mutex_lock(&usecnt_lock);
                usecnt++;
@@ -794,15 +830,6 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
                                tmp = NULL;
                        }
                }
-               pthread_create(&p->sound_thread, NULL, sound_thread, (void *) p);
-               alsa_state = snd_pcm_state(p->capture_handle);
-               if (alsa_state == SND_PCM_STATE_XRUN) {
-                       snd_pcm_prepare(p->capture_handle);
-               }
-               alsa_state = snd_pcm_state(p->capture_handle);
-               if (alsa_state == SND_PCM_STATE_PREPARED) {
-                       snd_pcm_start(p->capture_handle);
-               }
        }
        return tmp;
 }
@@ -871,7 +898,6 @@ static char autoanswer_usage[] =
 
 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) {
@@ -879,8 +905,9 @@ static int console_answer(int fd, int argc, char *argv[])
                return RESULT_FAILURE;
        }
        hookstate = 1;
-       ast_queue_frame(alsa.owner, &f);
-       answer_sound(&alsa);
+       cursound = -1;
+       needanswer++;
+       answer_sound();
        return RESULT_SUCCESS;
 }
 
@@ -891,7 +918,6 @@ static char sendtext_usage[] =
 static int console_sendtext(int fd, int argc, char *argv[])
 {
        int tmparg = 2;
-       struct ast_frame f = { 0, };
        if (argc < 2)
                return RESULT_SHOWUSAGE;
        if (!alsa.owner) {
@@ -905,13 +931,7 @@ static int console_sendtext(int fd, int argc, char *argv[])
                strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send));
                strncat(text2send, " ", sizeof(text2send) - strlen(text2send));
        }
-       if (strlen(text2send)) {
-                f.frametype = AST_FRAME_TEXT;
-                f.subclass = 0;
-                f.data = text2send;
-                f.datalen = strlen(text2send);
-                ast_queue_frame(alsa.owner, &f);
-        }
+       needanswer++;
        return RESULT_SUCCESS;
 }
 
@@ -923,8 +943,7 @@ static int console_hangup(int fd, int argc, char *argv[])
 {
        if (argc != 1)
                return RESULT_SHOWUSAGE;
-       alsa.cursound = -1;
-       alsa.nosound = 0;
+       cursound = -1;
        if (!alsa.owner && !hookstate) {
                ast_cli(fd, "No call to hangup up\n");
                return RESULT_FAILURE;
@@ -946,16 +965,13 @@ static int console_dial(int fd, int argc, char *argv[])
        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) {
-                       for (x=0;x<strlen(argv[1]);x++) {
-                                f.subclass = argv[1][x];
-                                ast_queue_frame(alsa.owner, &f);
-                        }
+                       strncat(digits, argv[1], sizeof(digits) - strlen(digits));
+                       /* Wake up the polling thread */
+                       write(cmd[1], &b, sizeof(b));
                } else {
                        ast_cli(fd, "You're already in a call.  You can use this only to dial digits until you hangup\n");
                        return RESULT_FAILURE;
@@ -1005,10 +1021,8 @@ int load_module()
        int flags;
        struct ast_config *cfg;
        struct ast_variable *v;
-#if 0
        res = pipe(cmd);
        res = pipe(sndcmd);
-       
        if (res) {
                ast_log(LOG_ERROR, "Unable to create pipe\n");
                return -1;
@@ -1017,7 +1031,16 @@ int load_module()
        fcntl(cmd[0], F_SETFL, flags | O_NONBLOCK);
        flags = fcntl(cmd[1], F_GETFL);
        fcntl(cmd[1], F_SETFL, flags | O_NONBLOCK);
-#endif
+       res = soundcard_init();
+       if (res < 0) {
+               close(cmd[1]);
+               close(cmd[0]);
+               if (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");
@@ -1044,24 +1067,20 @@ int load_module()
                                strncpy(language, v->value, sizeof(language)-1);
                        else if (!strcasecmp(v->name, "extension"))
                                strncpy(exten, v->value, sizeof(exten)-1);
-                       else if (!strcasecmp(v->name, "input_device")) {
+                       else if (!strcasecmp(v->name, "input_device"))
                                strncpy(indevname, v->value, sizeof(indevname)-1);
-                       } else if (!strcasecmp(v->name, "output_device"))
+                       else if (!strcasecmp(v->name, "output_device"))
                                strncpy(outdevname, v->value, sizeof(outdevname)-1);
                        v=v->next;
                }
                ast_destroy(cfg);
        }
-       res = soundcard_init();
-       if (res < 0) {
-               close(cmd[1]);
-               close(cmd[0]);
-               if (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;
+       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;
 }
 
@@ -1072,6 +1091,8 @@ int unload_module()
        int x;
        for (x=0;x<sizeof(myclis)/sizeof(struct ast_cli_entry); x++)
                ast_cli_unregister(myclis + x);
+       close(readdev);
+       close(writedev);
        if (cmd[0] > 0) {
                close(cmd[0]);
                close(cmd[1]);
@@ -1084,10 +1105,6 @@ int unload_module()
                ast_softhangup(alsa.owner, AST_SOFTHANGUP_APPUNLOAD);
        if (alsa.owner)
                return -1;
-       if (alsa.capture_buf) {
-               free (alsa.capture_buf);
-               alsa.capture_buf=0;
-       }
        return 0;
 }