diff options
Diffstat (limited to 'akode/lib/wav_decoder.cpp')
-rw-r--r-- | akode/lib/wav_decoder.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/akode/lib/wav_decoder.cpp b/akode/lib/wav_decoder.cpp new file mode 100644 index 0000000..73f663c --- /dev/null +++ b/akode/lib/wav_decoder.cpp @@ -0,0 +1,236 @@ +/* aKode: Wav-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 <string.h> + +#include "audioframe.h" +#include "decoder.h" +#include "file.h" +#include "wav_decoder.h" + +#include <iostream> + +namespace aKode { + +bool WavDecoderPlugin::canDecode(File* src) { + char header[4]; + bool res = false; + src->openRO(); + if (src->read(header, 4) != 4 || memcmp(header, "RIFF",4) != 0 ) goto close; + src->seek(8); + if (src->read(header, 4) != 4 || memcmp(header, "WAVE",4) != 0 ) goto close; + src->seek(20); + if (src->read(header, 2) != 2 || memcmp(header, "\x01\0",2) != 0 ) goto close; + res = true; +close: + src->close(); + return res; + +} + +extern "C" { WavDecoderPlugin wav_decoder; } + +struct WavDecoder::private_data +{ + private_data() : valid(false), buffer_length(0), buffer(0) {}; + AudioConfiguration config; + bool valid; + long position; + + long pos; + long length; + + unsigned int buffer_length; + unsigned char *buffer; + + File *src; +}; + + +WavDecoder::WavDecoder(File *src) { + d = new private_data; + + openFile(src); +} + +bool WavDecoder::openFile(File* src) { + d->src = src; + src->openRO(); + src->fadvise(); + + // Get format information + unsigned char buffer[4]; + src->seek(4); + src->read((char*)buffer, 4); // size of stream + d->length = buffer[0] + buffer[1]*256 + buffer[2] * (1<<16) + buffer[3] * (1<<24) + 8; + + src->seek(16); + src->read((char*)buffer, 4); // should really be 16 + d->pos = 20 + buffer[0] + buffer[1]*256; + if (buffer[2] != 0 || buffer[3] != 0) goto invalid; + + src->read((char*)buffer, 2); // check for compression + if (*(short*)buffer != 1) goto invalid; + + src->read((char*)buffer, 2); + d->config.channels = buffer[0] + buffer[1]*256; + if (d->config.channels <=2) + d->config.channel_config = MonoStereo; + else + d->config.channel_config = MultiChannel; + + src->read((char*)buffer, 4); + d->config.sample_rate = buffer[0] + buffer[1]*256 + buffer[2] * (1<<16) + buffer[3] * (1<<24); + + src->seek(34); + src->read((char*)buffer, 2); + d->config.sample_width = buffer[0] + buffer[1]*256; + + // Various sanity checks + if (d->config.sample_width != 8 && d->config.sample_width != 16 && d->config.sample_width != 32) goto invalid; + if (d->config.sample_rate > 200000) goto invalid; + +find_data: + src->seek(d->pos); + src->read((char*)buffer, 4); + if (memcmp(buffer, "data", 4) != 0) + if (memcmp(buffer, "clm ", 4) != 0) + goto invalid; + else { + src->read((char*)buffer, 4); + d->pos = d->pos+ 8 + buffer[0] + buffer[1]*256; + goto find_data; + } + + src->seek(d->pos+8); // start of data + d->position = 0; + d->valid = true; + // 1024 samples: + d->buffer_length = 1024*((d->config.sample_width+7)/8)*d->config.channels; + d->buffer = new unsigned char[d->buffer_length]; + return true; + +invalid: + std::cerr << "Invalid WAV file\n"; + d->valid = false; + src->close(); + return false; +} + +void WavDecoder::close() { + d->src->close(); + delete[] d->buffer; + d->valid = false; +} + +WavDecoder::~WavDecoder() { + delete d; +} + +bool WavDecoder::readFrame(AudioFrame* frame) +{ + if (!d->valid || eof()) return false; + + unsigned long samples = 1024; + // read a frame + unsigned long length; + length = d->src->read((char*)d->buffer, d->buffer_length); + if (length != d->buffer_length) { + samples = length / (d->config.channels * ((d->config.sample_width+7)/8)); + } + d->pos += length; + d->position += samples; + // Ensure the frame is properly configured + frame->reserveSpace(&d->config, samples); + + int channels = d->config.channels; + if (d->config.sample_width == 8) { + // WAV 8bit is unsigned + uint8_t* buffer = (uint8_t*)d->buffer; + int8_t** data = (int8_t**)frame->data; + for(unsigned int i=0; i<samples; i++) + for(int j=0; j<channels; j++) + data[j][i] = (int(buffer[i*channels+j])) - 128; + } + else + if (d->config.sample_width == 16) { + int16_t* buffer = (int16_t*)d->buffer; + int16_t** data = (int16_t**)frame->data; + for(unsigned int i=0; i<samples; i++) + for(int j=0; j<channels; j++) + data[j][i] = buffer[i*channels+j]; + } else + if (d->config.sample_width == 32) { + int32_t* buffer = (int32_t*)d->buffer; + int32_t** data = (int32_t**)frame->data; + for(unsigned int i=0; i<samples; i++) + for(int j=0; j<channels; j++) + data[j][i] = buffer[i*channels+j]; + } else + return false; + + frame->pos = position(); + + return true; +} + +long WavDecoder::length() { + if (!d->valid) return -1; + long byterate = d->config.sample_rate * d->config.channels * ((d->config.sample_width+7)/8); + return (d->length-44)/byterate; +} + +long WavDecoder::position() { + if (!d->valid) return -1; + long div = (d->position / d->config.sample_rate) * 1000; + long rem = (d->position % d->config.sample_rate) * 1000; + return div + (rem / d->config.sample_rate); +} + +bool WavDecoder::eof() { + if (!d->src) return true; + else return d->src->eof(); +} + +bool WavDecoder::error() { + return !d->valid; +} + +bool WavDecoder::seek(long pos) { + int samplesize = d->config.channels * ((d->config.sample_width+7)/8); + long byterate = d->config.sample_rate * samplesize; + long sample_pos = (pos * byterate) / 1000; + long byte_pos = sample_pos * samplesize; + if (byte_pos+44 >= d->length) return false; + if (!d->src->seek(byte_pos+44)) return false; + d->pos = byte_pos + 44; + return true; +} + +bool WavDecoder::seekable() { + return d->src->seekable(); +} + +const AudioConfiguration* WavDecoder::audioConfiguration() { + if (!d->valid) return 0; + return &d->config; +} + +} // namespace |