diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c new file mode 100755 index 0000000000..c6f3db3ebf --- /dev/null +++ b/codecs/codec_speex.c @@ -0,0 +1,314 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Translate between signed linear and Speex (Open Codec) + * + * Copyright (C) 2002, Digium + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * This work was motivated by Jeremy McNamera's + */ + +#define TYPE_SILENCE 0x2 +#define TYPE_HIGH 0x0 +#define TYPE_LOW 0x1 +#define TYPE_MASK 0x3 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Sample frame data */ +#include "slin_speex_ex.h" +#include "speex_slin_ex.h" + +static pthread_mutex_t localuser_lock = AST_MUTEX_INITIALIZER; +static int localusecnt=0; + +static char *tdesc = "Speex/PCM16 (signed linear) Codec Translator"; + +struct ast_translator_pvt { + void *speex; + struct ast_frame f; + SpeexBits bits; + int framesize; + /* Space to build offset */ + char offset[AST_FRIENDLY_OFFSET]; + /* Buffer for our outgoing frame */ + short outbuf[8000]; + /* Enough to store a full second */ + short buf[8000]; + int tail; +}; + +#define speex_coder_pvt ast_translator_pvt + +static struct ast_translator_pvt *lintospeex_new() +{ + struct speex_coder_pvt *tmp; + tmp = malloc(sizeof(struct speex_coder_pvt)); + if (tmp) { + if (!(tmp->speex = speex_encoder_init(&speex_nb_mode))) { + free(tmp); + tmp = NULL; + } else { + speex_bits_init(&tmp->bits); + speex_bits_reset(&tmp->bits); + speex_encoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize); + tmp->tail = 0; + } + localusecnt++; + } + return tmp; +} + +static struct ast_translator_pvt *speextolin_new() +{ + struct speex_coder_pvt *tmp; + tmp = malloc(sizeof(struct speex_coder_pvt)); + if (tmp) { + if (!(tmp->speex = speex_decoder_init(&speex_nb_mode))) { + free(tmp); + tmp = NULL; + } else { + speex_bits_init(&tmp->bits); + speex_decoder_ctl(tmp->speex, SPEEX_GET_FRAME_SIZE, &tmp->framesize); + tmp->tail = 0; + } + localusecnt++; + } + return tmp; +} + +static struct ast_frame *lintospeex_sample() +{ + static struct ast_frame f; + f.frametype = AST_FRAME_VOICE; + f.subclass = AST_FORMAT_SLINEAR; + f.datalen = sizeof(slin_speex_ex); + /* Assume 8000 Hz */ + f.timelen = sizeof(slin_speex_ex)/16; + f.mallocd = 0; + f.offset = 0; + f.src = __PRETTY_FUNCTION__; + f.data = slin_speex_ex; + return &f; +} + +static struct ast_frame *speextolin_sample() +{ + static struct ast_frame f; + f.frametype = AST_FRAME_VOICE; + f.subclass = AST_FORMAT_SPEEX; + f.datalen = sizeof(speex_slin_ex); + /* All frames are 20 ms long */ + f.timelen = 20; + f.mallocd = 0; + f.offset = 0; + f.src = __PRETTY_FUNCTION__; + f.data = speex_slin_ex; + return &f; +} + +static struct ast_frame *speextolin_frameout(struct ast_translator_pvt *tmp) +{ + if (!tmp->tail) + return NULL; + /* Signed linear is no particular frame size, so just send whatever + we have in the buffer in one lump sum */ + tmp->f.frametype = AST_FRAME_VOICE; + tmp->f.subclass = AST_FORMAT_SLINEAR; + tmp->f.datalen = tmp->tail * 2; + /* Assume 8000 Hz */ + tmp->f.timelen = tmp->tail / 8; + tmp->f.mallocd = 0; + tmp->f.offset = AST_FRIENDLY_OFFSET; + tmp->f.src = __PRETTY_FUNCTION__; + tmp->f.data = tmp->buf; + /* Reset tail pointer */ + tmp->tail = 0; + return &tmp->f; +} + +static int speextolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f) +{ + /* Assuming there's space left, decode into the current buffer at + the tail location. Read in as many frames as there are */ + int x; + int res; + float fout[1024]; + /* Read in bits */ + speex_bits_read_from(&tmp->bits, f->data, f->datalen); + for(;;) { + res = speex_decode(tmp->speex, &tmp->bits, fout); + if (res < 0) + break; + if (tmp->tail + tmp->framesize < sizeof(tmp->buf) / 2) { + for (x=0;xframesize;x++) { + tmp->buf[tmp->tail + x] = fout[x]; + } + tmp->tail += tmp->framesize; + } else { + ast_log(LOG_WARNING, "Out of buffer space\n"); + return -1; + } + + } + return 0; +} + +static int lintospeex_framein(struct ast_translator_pvt *tmp, struct ast_frame *f) +{ + /* Just add the frames to our stream */ + /* XXX We should look at how old the rest of our stream is, and if it + is too old, then we should overwrite it entirely, otherwise we can + get artifacts of earlier talk that do not belong */ + if (tmp->tail + f->datalen/2 < sizeof(tmp->buf) / 2) { + memcpy((tmp->buf + tmp->tail), f->data, f->datalen); + tmp->tail += f->datalen/2; + } else { + ast_log(LOG_WARNING, "Out of buffer space\n"); + return -1; + } + return 0; +} + +static struct ast_frame *lintospeex_frameout(struct ast_translator_pvt *tmp) +{ + float fbuf[1024]; + int len; + int y=0,x; + /* We can't work on anything less than a frame in size */ + if (tmp->tail < tmp->framesize) + return NULL; + tmp->f.frametype = AST_FRAME_VOICE; + tmp->f.subclass = AST_FORMAT_SPEEX; + tmp->f.mallocd = 0; + tmp->f.offset = AST_FRIENDLY_OFFSET; + tmp->f.src = __PRETTY_FUNCTION__; + tmp->f.data = tmp->outbuf; + speex_bits_reset(&tmp->bits); + while(tmp->tail >= tmp->framesize) { + /* Convert to floating point */ + for (x=0;xframesize;x++) + fbuf[x] = tmp->buf[x]; + /* Encode a frame of data */ + speex_encode(tmp->speex, fbuf, &tmp->bits); + /* Assume 8000 Hz -- 20 ms */ + tmp->tail -= tmp->framesize; + /* Move the data at the end of the buffer to the front */ + if (tmp->tail) + memmove(tmp->buf, tmp->buf + tmp->framesize, tmp->tail * 2); + y++; + } + /* Terminate bit stream */ + speex_bits_pack(&tmp->bits, 15, 5); + len = speex_bits_write(&tmp->bits, (char *)tmp->outbuf, sizeof(tmp->outbuf)); + tmp->f.datalen = len; + tmp->f.timelen = y * 20; +#if 0 + { + static int fd = -1; + if (fd < 0) { + fd = open("speex.raw", O_WRONLY|O_TRUNC|O_CREAT); + if (fd > -1) { + write(fd, tmp->f.data, tmp->f.datalen); + close(fd); + } + } + } +#endif + return &tmp->f; +} + +static void speextolin_destroy(struct ast_translator_pvt *pvt) +{ + speex_decoder_destroy(pvt->speex); + speex_bits_destroy(&pvt->bits); + free(pvt); + localusecnt--; +} + +static void lintospeex_destroy(struct ast_translator_pvt *pvt) +{ + speex_encoder_destroy(pvt->speex); + speex_bits_destroy(&pvt->bits); + free(pvt); + localusecnt--; +} + +static struct ast_translator speextolin = + { "speextolin", + AST_FORMAT_SPEEX, AST_FORMAT_SLINEAR, + speextolin_new, + speextolin_framein, + speextolin_frameout, + speextolin_destroy, + speextolin_sample + }; + +static struct ast_translator lintospeex = + { "lintospeex", + AST_FORMAT_SLINEAR, AST_FORMAT_SPEEX, + lintospeex_new, + lintospeex_framein, + lintospeex_frameout, + lintospeex_destroy, + lintospeex_sample + }; + +int unload_module(void) +{ + int res; + ast_pthread_mutex_lock(&localuser_lock); + res = ast_unregister_translator(&lintospeex); + if (!res) + res = ast_unregister_translator(&speextolin); + if (localusecnt) + res = -1; + ast_pthread_mutex_unlock(&localuser_lock); + return res; +} + +int load_module(void) +{ + int res; + res=ast_register_translator(&speextolin); + if (!res) + res=ast_register_translator(&lintospeex); + else + ast_unregister_translator(&speextolin); + return res; +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +}