/* * iaxclient: a portable telephony toolkit * * Copyright (C) 2003-2004, Horizon Wimba, Inc. * * Steve Kann * * This program is free software, distributed under the terms of * the GNU Lesser (Library) General Public License */ #include "codec_speex.h" #include "iaxclient_lib.h" #include "speex/speex.h" /* see codec_speex.h for termination mode definition; Zero is the "asterisk-compatible" method -- * generally, any other choice will not be compatible with established practice */ struct state { void *state; int frame_size; int termination_mode; SpeexBits bits; }; static void destroy ( struct iaxc_audio_codec *c) { struct state * encstate = (struct state *) c->encstate; struct state * decstate = (struct state *) c->decstate; speex_bits_destroy(&encstate->bits); speex_bits_destroy(&decstate->bits); speex_encoder_destroy(encstate->state); speex_decoder_destroy(decstate->state); free(c->encstate); free(c->decstate); free(c); } static int decode ( struct iaxc_audio_codec *c, int *inlen, unsigned char *in, int *outlen, short *out ) { struct state * decstate = (struct state *) c->decstate; int ret =0; int bits_left = 0; int bits_read = 0; int start_bits = 0; if(*inlen == 0) { //return 0; //fprintf(stderr, "Speex Interpolate\n"); speex_decode_int(decstate->state, NULL, out); *outlen -= decstate->frame_size; return 0; } /* XXX if the input contains more than we can read, we lose here */ speex_bits_read_from(&decstate->bits, (char *) in, *inlen); *inlen = 0; start_bits = speex_bits_remaining(&decstate->bits); while(speex_bits_remaining(&decstate->bits) && (*outlen >= decstate->frame_size)) { ret = speex_decode_int(decstate->state, &decstate->bits, out); // * from speex/speex.h, speex_decode returns: // * @return return status (0 for no error, -1 for end of stream, -2 other) if (ret == 0) { //fprintf(stderr, "decode: inlen=%d outlen=%d\n", *inlen, *outlen); /* one frame of output */ *outlen -= decstate->frame_size; out += decstate->frame_size; if(decstate->termination_mode == IAXC_SPEEX_TERMINATION_NONE) { bits_left = speex_bits_remaining(&decstate->bits) % 8; if(bits_left) speex_bits_advance(&decstate->bits, bits_left); } } else if (ret == -1) { /* at end of stream, or just a terminator */ bits_left = speex_bits_remaining(&decstate->bits) % 8; if(bits_left >= 5) speex_bits_advance(&decstate->bits, bits_left); else break; } else { /* maybe there's not a whole frame somehow? */ fprintf(stderr, "decode_int returned non-zero => %d\n",ret); break; } } return 0; } static int encode ( struct iaxc_audio_codec *c, int *inlen, short *in, int *outlen, unsigned char *out ) { int bytes; //int bitrate; struct state * encstate = (struct state *) c->encstate; /* need to encode minimum of encstate->frame_size samples */ if(encstate->termination_mode) { /* abnormal termination modes: separate frames, with or without termination in-between */ while(*inlen >= encstate->frame_size) { /* reset and encode*/ speex_bits_reset(&encstate->bits); speex_encode_int(encstate->state, in, &encstate->bits); /* add terminator */ if(encstate->termination_mode != IAXC_SPEEX_TERMINATION_NONE) speex_bits_pack(&encstate->bits, 15, 5); /* write to output */ /* can an error happen here? no bytes? */ bytes = speex_bits_write(&encstate->bits, (char *) out, *outlen); //fprintf(stderr, "encode wrote %d bytes, outlen was %d\n", bytes, *outlen); /* advance pointers to input and output */ *inlen -= encstate->frame_size; in += encstate->frame_size; *outlen -= bytes; out += bytes; } } else { /* only add terminator at end of bits */ speex_bits_reset(&encstate->bits); /* need to encode minimum of encstate->frame_size samples */ while(*inlen >= encstate->frame_size) { //fprintf(stderr, "encode: inlen=%d outlen=%d\n", *inlen, *outlen); speex_encode_int(encstate->state, in, &encstate->bits); *inlen -= encstate->frame_size; in += encstate->frame_size; } /* add terminator */ speex_bits_pack(&encstate->bits, 15, 5); bytes = speex_bits_write(&encstate->bits, (char *) out, *outlen); /* can an error happen here? no bytes? */ *outlen -= bytes; } return 0; } struct iaxc_audio_codec *iaxc_audio_codec_speex_new(struct iaxc_speex_settings *set) { struct state * encstate; struct state * decstate; struct iaxc_audio_codec *c = calloc(sizeof(struct iaxc_audio_codec),1); const SpeexMode *sm; if(!c) return c; strcpy(c->name,"speex"); c->format = IAXC_FORMAT_SPEEX; c->encode = encode; c->decode = decode; c->destroy = destroy; c->encstate = calloc(sizeof(struct state),1); c->decstate = calloc(sizeof(struct state),1); /* leaks a bit on no-memory */ if(!(c->encstate && c->decstate)) return NULL; encstate = (struct state *) c->encstate; decstate = (struct state *) c->decstate; sm = set->mode; if(!sm) sm = &speex_nb_mode; encstate->state = speex_encoder_init(sm); decstate->state = speex_decoder_init(sm); speex_bits_init(&encstate->bits); speex_bits_init(&decstate->bits); speex_bits_reset(&encstate->bits); speex_bits_reset(&decstate->bits); speex_decoder_ctl(decstate->state, SPEEX_SET_ENH, &set->decode_enhance); speex_encoder_ctl(encstate->state, SPEEX_SET_COMPLEXITY, &set->complexity); if(set->quality >= 0) { if(set->vbr) { speex_encoder_ctl(encstate->state, SPEEX_SET_VBR_QUALITY, &set->quality); } else { int quality = set->quality; speex_encoder_ctl(encstate->state, SPEEX_SET_QUALITY, &quality); } } if(set->bitrate >= 0) speex_encoder_ctl(encstate->state, SPEEX_SET_BITRATE, &set->bitrate); if(set->vbr) speex_encoder_ctl(encstate->state, SPEEX_SET_VBR, &set->vbr); if(set->abr) speex_encoder_ctl(encstate->state, SPEEX_SET_ABR, &set->abr); /* set up frame sizes (normally, this is 20ms worth) */ speex_encoder_ctl(encstate->state,SPEEX_GET_FRAME_SIZE,&encstate->frame_size); speex_encoder_ctl(decstate->state,SPEEX_GET_FRAME_SIZE,&decstate->frame_size); /* XXX: for some reason, with narrowband, we get 0 for decstate sometimes? */ if(!decstate->frame_size) decstate->frame_size = 160; if(!encstate->frame_size) encstate->frame_size = 160; c->minimum_frame_size = 160; if(encstate->frame_size > c->minimum_frame_size) c->minimum_frame_size = encstate->frame_size; if(decstate->frame_size > c->minimum_frame_size) c->minimum_frame_size = decstate->frame_size; encstate->termination_mode = set->termination_mode; decstate->termination_mode = set->termination_mode; if(!(encstate->state && decstate->state)) return NULL; return c; }