245 lines
5.8 KiB
C
245 lines
5.8 KiB
C
|
/*****************************************************************************
|
||
|
*
|
||
|
* This program is free software ; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*
|
||
|
* $Id: speexcodec.c 620 2006-01-31 14:46:34Z picard $
|
||
|
*
|
||
|
* The Core Pocket Media Player
|
||
|
* Copyright (c) 2004-2005 Gabor Kovacs
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "../common/common.h"
|
||
|
#include "speex.h"
|
||
|
#include <speex/speex_header.h>
|
||
|
#include <speex/speex_stereo.h>
|
||
|
#include <speex/speex_callbacks.h>
|
||
|
|
||
|
#define MAX_FRAME_SIZE 2000
|
||
|
|
||
|
typedef struct speex
|
||
|
{
|
||
|
codec Codec;
|
||
|
int ExtraHeaders;
|
||
|
int AfterFlush;
|
||
|
int FrameSize;
|
||
|
int Frames;
|
||
|
int FramesPerPacket;
|
||
|
void *Decoder;
|
||
|
SpeexBits Bits;
|
||
|
SpeexStereoState Stereo;
|
||
|
spx_int16_t Output[MAX_FRAME_SIZE];
|
||
|
|
||
|
} speex;
|
||
|
|
||
|
static int UpdateInput(speex* p)
|
||
|
{
|
||
|
if (p->Decoder)
|
||
|
{
|
||
|
speex_decoder_destroy(p->Decoder);
|
||
|
speex_bits_destroy(&p->Bits);
|
||
|
p->Decoder = NULL;
|
||
|
}
|
||
|
|
||
|
if (p->Codec.In.Format.Type == PACKET_AUDIO)
|
||
|
{
|
||
|
int Enabled = 1;
|
||
|
const SpeexMode *Mode;
|
||
|
SpeexHeader *Header;
|
||
|
SpeexCallback Callback;
|
||
|
|
||
|
if (p->Codec.In.Format.ExtraLength < 8)
|
||
|
{
|
||
|
if (p->Codec.In.Format.Format.Audio.SampleRate > 25000)
|
||
|
Mode = &speex_uwb_mode; // ideal 32000hz
|
||
|
else if (p->Codec.In.Format.Format.Audio.SampleRate > 12500)
|
||
|
Mode = &speex_wb_mode; // ideal 16000hz
|
||
|
else
|
||
|
Mode = &speex_nb_mode; // ideal 8000hz
|
||
|
|
||
|
Header = malloc(sizeof(SpeexHeader));
|
||
|
if (Header)
|
||
|
speex_init_header(Header, p->Codec.In.Format.Format.Audio.SampleRate, p->Codec.In.Format.Format.Audio.Channels, Mode);
|
||
|
}
|
||
|
else
|
||
|
Header = speex_packet_to_header(p->Codec.In.Format.Extra,p->Codec.In.Format.ExtraLength);
|
||
|
|
||
|
if (!Header || Header->mode >= SPEEX_NB_MODES || Header->speex_version_id > 1)
|
||
|
{
|
||
|
free(Header);
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
Mode = speex_lib_get_mode(Header->mode);
|
||
|
if (Mode->bitstream_version != Header->mode_bitstream_version)
|
||
|
{
|
||
|
free(Header);
|
||
|
return ERR_NOT_SUPPORTED;
|
||
|
}
|
||
|
|
||
|
p->Decoder = speex_decoder_init(Mode);
|
||
|
if (!p->Decoder)
|
||
|
{
|
||
|
free(Header);
|
||
|
return ERR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
|
||
|
p->FramesPerPacket = max(1,Header->frames_per_packet);
|
||
|
p->ExtraHeaders = Header->extra_headers;
|
||
|
|
||
|
speex_decoder_ctl(p->Decoder, SPEEX_SET_ENH, &Enabled);
|
||
|
speex_decoder_ctl(p->Decoder, SPEEX_GET_FRAME_SIZE, &p->FrameSize);
|
||
|
|
||
|
#ifdef NO_FLOATINGPOINT
|
||
|
Header->nb_channels = 1; //force mono. stereo would need floating point calculations
|
||
|
#endif
|
||
|
|
||
|
p->Codec.In.Format.Format.Audio.SampleRate = Header->rate;
|
||
|
p->Codec.In.Format.Format.Audio.Channels = Header->nb_channels;
|
||
|
|
||
|
if (Header->nb_channels != 1)
|
||
|
{
|
||
|
Callback.callback_id = SPEEX_INBAND_STEREO;
|
||
|
Callback.func = speex_std_stereo_request_handler;
|
||
|
Callback.data = &p->Stereo;
|
||
|
speex_decoder_ctl(p->Decoder, SPEEX_SET_HANDLER, &Callback);
|
||
|
}
|
||
|
|
||
|
speex_decoder_ctl(p->Decoder, SPEEX_SET_SAMPLING_RATE, &Header->rate);
|
||
|
|
||
|
speex_bits_init(&p->Bits);
|
||
|
|
||
|
PacketFormatPCM(&p->Codec.Out.Format,&p->Codec.In.Format,15);
|
||
|
|
||
|
free(Header);
|
||
|
}
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static int Process(speex* p, const packet* Packet, const flowstate* State)
|
||
|
{
|
||
|
int Result;
|
||
|
if (Packet)
|
||
|
{
|
||
|
if (Packet->RefTime >= 0)
|
||
|
{
|
||
|
if (Packet->RefTime == 0)
|
||
|
p->AfterFlush = 0;
|
||
|
p->Codec.Packet.RefTime = Packet->RefTime;
|
||
|
}
|
||
|
|
||
|
if (p->ExtraHeaders>0)
|
||
|
{
|
||
|
--p->ExtraHeaders;
|
||
|
return ERR_NEED_MORE_DATA;
|
||
|
}
|
||
|
|
||
|
speex_bits_read_from(&p->Bits, (char*)Packet->Data[0], Packet->Length);
|
||
|
p->Frames = p->FramesPerPacket;
|
||
|
}
|
||
|
else
|
||
|
p->Codec.Packet.RefTime = TIME_UNKNOWN;
|
||
|
|
||
|
if (p->Frames<=0)
|
||
|
return ERR_NEED_MORE_DATA;
|
||
|
|
||
|
Result = speex_decode_int(p->Decoder, &p->Bits, p->Output);
|
||
|
if (Result==-1)
|
||
|
{
|
||
|
p->Frames = 0;
|
||
|
return ERR_NEED_MORE_DATA;
|
||
|
}
|
||
|
|
||
|
if (Result==-2 || speex_bits_remaining(&p->Bits)<0)
|
||
|
{
|
||
|
p->Frames = 0;
|
||
|
return ERR_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
if (p->Codec.In.Format.Format.Audio.Channels==2)
|
||
|
speex_decode_stereo_int(p->Output, p->FrameSize, &p->Stereo);
|
||
|
|
||
|
p->Codec.Packet.Length = p->FrameSize * p->Codec.In.Format.Format.Audio.Channels * sizeof(int16_t);
|
||
|
p->Codec.Packet.Data[0] = p->Output;
|
||
|
--p->Frames;
|
||
|
|
||
|
if (p->AfterFlush)
|
||
|
{
|
||
|
// don't output this frame
|
||
|
--p->AfterFlush;
|
||
|
return Process(p,NULL,State);
|
||
|
}
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static int Flush(speex* p)
|
||
|
{
|
||
|
p->AfterFlush = 4;
|
||
|
p->Frames = 0;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static int Create(speex* p)
|
||
|
{
|
||
|
p->Codec.Process = (packetprocess)Process;
|
||
|
p->Codec.UpdateInput = (nodefunc)UpdateInput;
|
||
|
p->Codec.Flush = (nodefunc)Flush;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
static const nodedef Speex =
|
||
|
{
|
||
|
sizeof(speex),
|
||
|
SPEEX_ID,
|
||
|
CODEC_CLASS,
|
||
|
PRI_DEFAULT,
|
||
|
(nodecreate)Create,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static const nodedef SPX_HQ =
|
||
|
{
|
||
|
0, // parent size
|
||
|
SPX_HQ_ID,
|
||
|
FOURCC('O','G','H','V'),
|
||
|
PRI_DEFAULT,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static const nodedef SPX_LQ =
|
||
|
{
|
||
|
0, // parent size
|
||
|
SPX_LQ_ID,
|
||
|
FOURCC('O','G','G','V'),
|
||
|
PRI_DEFAULT,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
void Speex_Init()
|
||
|
{
|
||
|
NodeRegisterClass(&Speex);
|
||
|
NodeRegisterClass(&SPX_LQ);
|
||
|
NodeRegisterClass(&SPX_HQ);
|
||
|
}
|
||
|
|
||
|
void Speex_Done()
|
||
|
{
|
||
|
NodeUnRegisterClass(SPEEX_ID);
|
||
|
NodeUnRegisterClass(SPX_LQ_ID);
|
||
|
NodeUnRegisterClass(SPX_HQ_ID);
|
||
|
}
|