/* * iaxclient: a cross-platform IAX softphone library * * Copyrights: * Copyright (C) 2003-2004, Horizon Wimba, Inc. * Copyright (C) 2007, Wimba, Inc. * * Contributors: * 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" struct State { void *state; int frame_size; 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; if ( *inlen == 0 ) { speex_decode_int(decstate->state, NULL, out); *outlen -= decstate->frame_size; return 0; } speex_bits_read_from(&decstate->bits, (char *) in, *inlen); *inlen = 0; while ( speex_bits_remaining(&decstate->bits) && *outlen >= decstate->frame_size ) { int 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) { /* one frame of output */ *outlen -= decstate->frame_size; out += decstate->frame_size; } else if (ret == -1) { /* at end of stream, or just a terminator */ int 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; struct State * encstate = (struct State *) c->encstate; /* need to encode minimum of encstate->frame_size samples */ /* 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 *codec_audio_speex_new(struct iaxc_speex_settings *set) { struct State * encstate; struct State * decstate; struct iaxc_audio_codec *c = (struct iaxc_audio_codec *)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 = speex_lib_get_mode(SPEEX_MODEID_NB); 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 = (int)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_decoder_ctl(decstate->state,SPEEX_GET_FRAME_SIZE,&decstate->frame_size); 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; if(!(encstate->state && decstate->state)) return NULL; return c; }