diff options
Diffstat (limited to 'akode/plugins/xiph_decoder/vorbis_decoder.cpp')
-rw-r--r-- | akode/plugins/xiph_decoder/vorbis_decoder.cpp | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/akode/plugins/xiph_decoder/vorbis_decoder.cpp b/akode/plugins/xiph_decoder/vorbis_decoder.cpp new file mode 100644 index 0000000..44329d0 --- /dev/null +++ b/akode/plugins/xiph_decoder/vorbis_decoder.cpp @@ -0,0 +1,277 @@ +/* aKode: Ogg Vorbis-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_OGG_VORBIS + +#include <vorbis/vorbisfile.h> + +#include "file.h" +#include "audioframe.h" +#include "decoder.h" +#include "vorbis_decoder.h" + +//#include <iostream> + +namespace aKode { + +static size_t _read(void *ptr, size_t size, size_t nmemb, void *datasource) { + File *src = (File*)datasource; + return src->read((char*)ptr, size*nmemb); +} + +static int _seek(void *datasource, ogg_int64_t offset, int whence) { + File *src = (File*)datasource; + if (src->seek(offset, whence)) + return 0; + else + return -1; +} + +static int _close(void *datasource) { + File *src = (File*)datasource; + src->close(); + return 0; +} + +static long _tell(void *datasource) { + File *src = (File*)datasource; + return src->position(); +} + +static ov_callbacks _callbacks = {_read, _seek, _close, _tell}; + +bool VorbisDecoderPlugin::canDecode(File* src) { + OggVorbis_File vf; + src->openRO(); + int r = ov_test_callbacks(src, &vf, 0, 0, _callbacks); + ov_clear(&vf); + src->close(); + return r==0; +} + +extern "C" { VorbisDecoderPlugin vorbis_decoder; } + + +struct VorbisDecoder::private_data +{ + private_data() : bitstream(0), eof(false), error(false), initialized(false), retries(0), big_endian(0) {}; + OggVorbis_File *vf; + vorbis_comment *vc; + vorbis_info *vi; + + File *src; + AudioConfiguration config; + + int bitstream; + bool eof, error; + char buffer[8192]; + bool initialized; + int retries; + + int big_endian; +}; + +VorbisDecoder::VorbisDecoder(File *src) { + m_data = new private_data; + m_data->vf = new OggVorbis_File; + + m_data->src = src; + m_data->src->openRO(); + m_data->src->fadvise(); + + unsigned short endian_test = 1; + m_data->big_endian = 1 - (*((char *)(&endian_test))); +} + +VorbisDecoder::~VorbisDecoder() { + if (m_data->initialized) + ov_clear(m_data->vf); + delete m_data->vf; + delete m_data; +} + +static void setAudioConfiguration(AudioConfiguration *config, vorbis_info *vi) +{ + config->channels = vi->channels; + config->sample_rate = vi->rate; + config->sample_width = 16; + + if (config->channels <= 2) { + config->channel_config = MonoStereo; + config->surround_config = 0; + } else + if (config->channels <= 6) { + config->channel_config = Surround; + SurroundConfiguration surround_config; + switch (config->channels) { + case 3: + surround_config.front_channels = 3; + break; + case 4: + surround_config.front_channels = 2; + surround_config.rear_channels = 2; + break; + case 5: + surround_config.front_channels = 3; + surround_config.rear_channels = 2; + break; + case 6: + surround_config.front_channels = 3; + surround_config.rear_channels = 2; + surround_config.LFE_channel = 1; + break; + } + config->surround_config = surround_config; + } + else { + config->channel_config = MultiChannel; + config->surround_config = 0; + } +} + +bool VorbisDecoder::openFile() { + int status; + + status = ov_open_callbacks(m_data->src, m_data->vf, 0, 0, _callbacks); + if (status != 0) goto fault; + + m_data->vi = ov_info(m_data->vf, -1); + //m_data->vc = ov_comment(m_data->vf, -1); + setAudioConfiguration(&m_data->config, m_data->vi); + + m_data->initialized = true; + m_data->error = false; + m_data->retries = 0; + return true; +fault: + m_data->initialized = false; + m_data->error = true; + return false; +} + +// translation from vorbis channel-layout to akodelib channel-layout +static int vorbis_channel[7][6] = { + {-1, -1, -1, -1, -1, -1}, + {0, -1, -1, -1, -1, -1}, + {0, 1, -1, -1, -1, -1}, + {0, 2, 1, -1, -1, -1}, + {0, 1, 2, 3, -1, -1}, + {0, 2, 1, 3, 4, -1}, + {0, 2, 1, 3, 4, 5} +}; + +bool VorbisDecoder::readFrame(AudioFrame* frame) +{ + if (!m_data->initialized) { + if (!openFile()) return false; + } + + int old_bitstream = m_data->bitstream; + long v = ov_read(m_data->vf, (char*)m_data->buffer, 8192, m_data->big_endian, 2, 1, &m_data->bitstream); + + if (v == 0 || v == OV_EOF ) { + // vorbisfile sometimes return 0 even though EOF is not yet reached + if (m_data->src->eof() || m_data->src->error() || ++m_data->retries >= 16) + m_data->eof = true; +// std::cerr << "akode-vorbis: EOF\n"; + } + else + if (v == OV_HOLE) { + if (++m_data->retries >= 16) m_data->error = true; +// std::cerr << "akode-vorbis: Hole\n"; + } + else + if (v < 0) { + m_data->error = true; +// std::cerr << "akode-vorbis: Error\n"; + } + + if (v <= 0) return false; + m_data->retries = 0; + + if (old_bitstream != m_data->bitstream) { // changing streams, update info + m_data->vi = ov_info(m_data->vf, -1); + //m_data->vc = ov_comment(m_data->vf, -1); + setAudioConfiguration(&m_data->config, m_data->vi); + } + + int channels = m_data->config.channels; + long length = v/(channels*2); + frame->reserveSpace(&m_data->config, length); + + // Demux into frame + int16_t* buffer = (int16_t*)m_data->buffer; + int16_t** data = (int16_t**)frame->data; + if (channels <= 6) { + int *trans = vorbis_channel[channels]; + for(int i=0; i<length; i++) + for(int j=0; j<channels; j++) + data [trans[j]] [i] = buffer[i*channels+j]; + } + else + for(int i=0; i<length; i++) + for(int j=0; j<channels; j++) + data[j][i] = buffer[i*channels+j]; + + frame->pos = position(); + return true; +} + +long VorbisDecoder::length() { + if (!m_data->initialized) return -1; + // -1 return total length of all bitstreams. + // Should we take them one at a time instead? + double ogglen = ov_time_total(m_data->vf,-1); + return (long)(ogglen*1000.0); +} + +long VorbisDecoder::position() { + if (!m_data->initialized) return -1; + double oggpos = ov_time_tell(m_data->vf); + return (long)(oggpos*1000.0); +} + +bool VorbisDecoder::eof() { + return m_data->eof; +} + +bool VorbisDecoder::error() { + return m_data->error; +} + +bool VorbisDecoder::seekable() { + return m_data->src->seekable(); +} + +bool VorbisDecoder::seek(long pos) { + if (!m_data->initialized) return false; + int r = ov_time_seek(m_data->vf, pos/1000.0); + return r == 0; +} + +const AudioConfiguration* VorbisDecoder::audioConfiguration() { + if (!m_data->initialized) return 0; + return &m_data->config; +} + +} // namespace + +#endif //OGG_VORBIS |