/* aKode: Speex-Decoder Copyright (C) 2004 Allan Sandfeld Jensen 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 #include #include #include #include #include #include } #ifdef SPEEX_DEBUG #include 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; iframe_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; iout_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->seek(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