flightgear/3rdparty/iaxclient/lib/codec_speex.c
2022-10-20 20:29:11 +08:00

191 lines
4.8 KiB
C

/*
* iaxclient: a cross-platform IAX softphone library
*
* Copyrights:
* Copyright (C) 2003-2004, Horizon Wimba, Inc.
* Copyright (C) 2007, Wimba, Inc.
*
* Contributors:
* Steve Kann <stevek@stevek.com>
*
* 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;
}