You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
akode/akode/plugins/xiph_decoder/speex_decoder.cpp

352 lines
10 KiB

/* aKode: Speex-Decoder
Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "akodelib.h"
#ifdef HAVE_SPEEX
extern "C" {
#include <string.h>
#include <stdlib.h>
#include <speex.h>
#include <speex_header.h>
#include <speex_callbacks.h>
#include <speex_stereo.h>
#include <ogg/ogg.h>
}
#ifdef SPEEX_DEBUG
#include <iostream>
using std::cerr;
#endif
#include "file.h"
#include "audioframe.h"
#include "decoder.h"
#include "speex_decoder.h"
namespace aKode {
bool SpeexDecoderPlugin::canDecode(File* src) {
char header[36];
bool res = false;
src->openRO();
if (src->read(header, 36) == 36)
if (memcmp(header, "OggS",4) == 0 )
if (memcmp(header+28, "Speex ",8) == 0) res = true;
src->close();
return res;
}
extern "C" { SpeexDecoderPlugin speex_decoder; }
struct SpeexDecoder::private_data
{
SpeexBits bits;
SpeexMode *mode;
SpeexStereoState stereo;
ogg_sync_state sync;
ogg_stream_state stream;
ogg_page page; // current page
ogg_packet packet; // current packet
void *dec_state;
File *src;
#ifdef HAVE_SPEEX11
int16_t *out_buffer;
#else
float *out_buffer;
#endif
unsigned int bitrate;
int frame_size;
int frames_per_packet;
int frame_nr;
AudioConfiguration config;
int serialno;
long position;
bool seeked;
bool initialized;
bool error, eof;
};
SpeexDecoder::SpeexDecoder(File *src) {
m_data = new private_data;
m_data->src = src;
m_data->out_buffer = 0;
ogg_sync_init(&m_data->sync);
m_data->dec_state = 0;
SpeexStereoState initstereo = SPEEX_STEREO_STATE_INIT;
m_data->stereo = initstereo;
m_data->initialized = m_data->eof = m_data->error = false;
m_data->frame_nr = 100000;
m_data->position = 0;
m_data->seeked = false;
src->openRO();
src->fadvise();
}
SpeexDecoder::~SpeexDecoder() {
if (m_data->initialized) {
speex_bits_reset(&m_data->bits);
ogg_sync_clear(&m_data->sync);
ogg_stream_clear(&m_data->stream);
if (m_data->dec_state) speex_decoder_destroy(m_data->dec_state);
m_data->src->close();
delete[] m_data->out_buffer;
}
delete m_data;
}
bool SpeexDecoder::openFile() {
m_data->error = false;
while(ogg_sync_pageout(&m_data->sync, &m_data->page) != 1) {
char *buf = ogg_sync_buffer(&m_data->sync, 1024);
int read = m_data->src->read(buf, 1024);
if (read <= 0) {
m_data->error = true;
return false;
}
ogg_sync_wrote(&m_data->sync, read);
}
m_data->serialno = ogg_page_serialno(&m_data->page);
ogg_stream_init(&m_data->stream, m_data->serialno);
speex_bits_init(&m_data->bits);
// ogg_stream_pagein(&m_data->stream, &m_data->page);
//ogg_stream_packetout(&m_data->stream, &m_data->packet);
// ogg_stream_packetout(&m_data->stream, &m_data->packet);
if(!decodeHeader())
{
m_data->error = true;
return false;
}
m_data->initialized = true;
return true;
}
bool SpeexDecoder::readPage() {
while (ogg_sync_pageout(&m_data->sync, &m_data->page) != 1) {
char *buf = ogg_sync_buffer(&m_data->sync, 4096);
long read = m_data->src->read(buf, 4096);
if (read <= 0) return false;
ogg_sync_wrote(&m_data->sync, read);
}
ogg_stream_pagein(&m_data->stream, &m_data->page);
// m_data->packets = ogg_page_packets(&m_data->page);
return true;
}
bool SpeexDecoder::readPacket() {
bool res = true;
while (ogg_stream_packetpeek(&m_data->stream, &m_data->packet) != 1 && res) {
res = readPage();
}
ogg_stream_packetout(&m_data->stream, &m_data->packet);
speex_bits_read_from(&m_data->bits, (char*)m_data->packet.packet, m_data->packet.bytes);
m_data->frame_nr = 0;
return res;
}
bool SpeexDecoder::decodeHeader() {
SpeexHeader *header;
header = speex_packet_to_header((char*)m_data->page.body, m_data->page.body_len);
if (!header) {
// invalid file
m_data->error = true;
#ifdef SPEEX_DEBUG
std::cerr << "Invalid file\n";
#endif
return false;
}
SpeexMode *mode = ( SpeexMode* )speex_mode_list[ header->mode ];
m_data->mode = mode;
m_data->config.channels = header->nb_channels;
m_data->config.channel_config = MonoStereo;
m_data->frames_per_packet = header->frames_per_packet;
if (mode->bitstream_version != header->mode_bitstream_version) {
// incompatible bitstream
m_data->error = true;
return false;
}
m_data->dec_state = speex_decoder_init(mode);
speex_decoder_ctl(m_data->dec_state, SPEEX_GET_FRAME_SIZE, &m_data->frame_size);
//m_data->bitrate = header->bitrate;
speex_decoder_ctl(m_data->dec_state, SPEEX_GET_BITRATE, &m_data->bitrate);
m_data->config.sample_rate = header->rate;
m_data->config.sample_width = 16;
speex_decoder_ctl(m_data->dec_state, SPEEX_SET_SAMPLING_RATE, &m_data->config.sample_rate);
//speex_decoder_ctl(m_data->dec_state, SPEEX_GET_SAMPLING_RATE, &m_data->sample_rate);
// Use the perceptial enhancement, which gives a subjectively better result
// but is technically further from the source.
int i = 1;
speex_decoder_ctl(m_data->dec_state, SPEEX_SET_ENH, &i);
// Handle the patched-on stereo stuff
if (m_data->config.channels != 1) {
SpeexCallback callback;
callback.callback_id = SPEEX_INBAND_STEREO;
callback.func = speex_std_stereo_request_handler;
callback.data = &m_data->stereo;
speex_decoder_ctl(m_data->dec_state, SPEEX_SET_HANDLER, &callback);
}
#ifdef HAVE_SPEEX11
m_data->out_buffer = new int16_t[m_data->frame_size*m_data->config.channels];
#else
m_data->out_buffer = new float[m_data->frame_size*m_data->config.channels];
#endif
free(header);
return true;
}
bool SpeexDecoder::readFrame(AudioFrame* frame)
{
if (!m_data->initialized) openFile();
if (m_data->eof || m_data->error) return false;
if (m_data->frame_nr >= m_data->frames_per_packet) {
if (!readPacket()) {
m_data->eof = true;
return false;
}
}
#if defined(HAVE_SPEEX11) && !defined(BROKEN_SPEEX11)
speex_decode_int(m_data->dec_state, &m_data->bits, m_data->out_buffer);
#else
speex_decode(m_data->dec_state, &m_data->bits, m_data->out_buffer);
#endif
int channels = m_data->config.channels;
int length = m_data->frame_size;
frame->reserveSpace(&m_data->config, length);
if (m_data->config.channels == 2)
#if defined(HAVE_SPEEX11) && !defined(BROKEN_SPEEX11)
speex_decode_stereo_int(m_data->out_buffer, length, &m_data->stereo);
#else
speex_decode_stereo(m_data->out_buffer, length, &m_data->stereo);
#endif
for (int i=0; i<m_data->frame_size*m_data->config.channels; i++) {
if (m_data->out_buffer[i] > 32766) m_data->out_buffer[i] = 32767;
else
if (m_data->out_buffer[i] < -32767) m_data->out_buffer[i] = -32768;
else
m_data->out_buffer[i] = m_data->out_buffer[i];
};
// Decode into frame
int16_t** data = (int16_t**)frame->data;
for(int i=0; i<length; i++)
for(int j=0; j<channels; j++)
#if defined(HAVE_SPEEX11)
data[j][i] = m_data->out_buffer[i*channels+j];
#else
data[j][i] = (int16_t)(m_data->out_buffer[i*channels+j]+0.5);
#endif
m_data->position += m_data->frame_size;
frame->pos = position();
m_data->frame_nr++;
return true;
}
long SpeexDecoder::length() {
if (!m_data->bitrate || !m_data->initialized) return -1;
float spxlen = (8.0*m_data->src->length())/(float)m_data->bitrate;
return (long)(spxlen*1000.0);
}
long SpeexDecoder::position() {
if (!m_data->bitrate || !m_data->initialized) return -1;
float spxpos = ((float)m_data->position)/(float)m_data->config.sample_rate;
if (m_data->seeked) {
float tellpos = (8.0*m_data->src->position())/(float)m_data->bitrate;
// tellpos should always be somewhat ahead, if spxpos is worse use tellpos
if (tellpos < spxpos) {
spxpos = tellpos;
m_data->position = (long)(tellpos*m_data->config.sample_rate);
}
}
return (long)(spxpos*1000.0);
}
bool SpeexDecoder::eof() {
return m_data->eof || m_data->error;
/*return m_data->error || (m_data->src->eof() && m_data->frame_nr >= m_data->frames_per_packet); */
}
bool SpeexDecoder::error() {
return m_data->error;
}
bool SpeexDecoder::seekable() {
return m_data->src->seekable();
}
bool SpeexDecoder::seek(long pos) {
if(!m_data->initialized) return false;
long bpos = (long)(((float)pos*(float)m_data->bitrate)/8000.0);
if (m_data->src->lseek(bpos)) {
speex_bits_reset(&m_data->bits);
ogg_sync_reset(&m_data->sync);
ogg_stream_reset(&m_data->stream);
readPage();
readPacket();
// We should now have read in a whole new page
bpos = (m_data->src->position()-m_data->page.body_len);
m_data->position = (long)((bpos*8.0*m_data->config.sample_rate)/(float)m_data->bitrate);
m_data->seeked = true;
return true;
}
return false;
}
const AudioConfiguration* SpeexDecoder::audioConfiguration() {
return &m_data->config;
}
} // namespace
#endif // HAVE_SPEEX