/* Audio File Library Copyright (C) 2001, Silicon Graphics, Inc. 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. */ /* ima.c This module implements IMA ADPCM compression for the Audio File Library. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "afinternal.h" #include "modules.h" #include "units.h" #include "compression.h" #include "byteorder.h" #include "util.h" #include "adpcm.h" #define CHNK(X) static _AFmodule ima_adpcm_decompress; typedef struct { _Track *track; AFvirtualfile *fh; int blockAlign, samplesPerBlock; AFframecount framesToIgnore; } ima_adpcm_data; static int ima_adpcm_decode_block (ima_adpcm_data *ima, u_int8_t *encoded, int16_t *decoded) { int outputLength; struct adpcm_state state; outputLength = ima->samplesPerBlock * sizeof (int16_t) * ima->track->f.channelCount; state.valprev = (encoded[1]<<8) | encoded[0]; if (encoded[1] & 0x80) state.valprev -= 0x10000; state.index = encoded[2]; *decoded++ = state.valprev; encoded += 4; _af_adpcm_decoder(encoded, decoded, ima->samplesPerBlock - 1, &state); return outputLength; } bool _af_ima_adpcm_format_ok (_AudioFormat *f) { if (f->channelCount != 1) { _af_error(AF_BAD_COMPRESSION, "IMA ADPCM compression requires 1 channel"); return AF_FALSE; } if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP || f->sampleWidth != 16) { _af_error(AF_BAD_COMPRESSION, "IMA ADPCM compression requires 16-bit signed integer format"); f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 16; /* non-fatal */ } if (f->byteOrder != AF_BYTEORDER_BIGENDIAN) { _af_error(AF_BAD_COMPRESSION, "IMA ADPCM compression requires big endian format"); f->byteOrder = AF_BYTEORDER_BIGENDIAN; /* non-fatal */ } return AF_TRUE; } static void ima_adpcm_decompress_describe (_AFmoduleinst *i) { /* XXXmpruett this is probably the correct way to go, but other things need to be changed first. i->outc->f.byteOrder = _AF_BYTEORDER_NATIVE; */ i->outc->f.compressionType = AF_COMPRESSION_NONE; i->outc->f.compressionParams = AU_NULL_PVLIST; } _AFmoduleinst _af_ima_adpcm_init_decompress (_Track *track, AFvirtualfile *fh, bool seekok, bool headerless, AFframecount *chunkframes) { _AFmoduleinst ret = _AFnewmodinst(&ima_adpcm_decompress); ima_adpcm_data *d; AUpvlist pv; int i; long l; void *v; assert(af_ftell(fh) == track->fpos_first_frame); d = (ima_adpcm_data *) _af_malloc(sizeof (ima_adpcm_data)); d->track = track; d->fh = fh; d->track->frames2ignore = 0; d->track->fpos_next_frame = d->track->fpos_first_frame; pv = d->track->f.compressionParams; if (_af_pv_getlong(pv, _AF_SAMPLES_PER_BLOCK, &l)) d->samplesPerBlock = l; else _af_error(AF_BAD_CODEC_CONFIG, "samples per block not set"); if (_af_pv_getlong(pv, _AF_BLOCK_SIZE, &l)) d->blockAlign = l; else _af_error(AF_BAD_CODEC_CONFIG, "block size not set"); *chunkframes = d->samplesPerBlock / d->track->f.channelCount; ret.modspec = d; return ret; } static void ima_adpcm_run_pull (_AFmoduleinst *module) { ima_adpcm_data *d = (ima_adpcm_data *) module->modspec; AFframecount frames2read = module->outc->nframes; AFframecount nframes = 0; int i, framesPerBlock, blockCount; ssize_t blocksRead, bytesDecoded; framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; assert(module->outc->nframes % framesPerBlock == 0); blockCount = module->outc->nframes / framesPerBlock; /* Read the compressed frames. */ blocksRead = af_fread(module->inc->buf, d->blockAlign, blockCount, d->fh); /* This condition would indicate that the file is bad. */ if (blocksRead < 0) { if (d->track->filemodhappy) { _af_error(AF_BAD_READ, "file missing data"); d->track->filemodhappy = AF_FALSE; } } if (blocksRead < blockCount) blockCount = blocksRead; /* Decompress into module->outc. */ for (i=0; iinc->buf + i * d->blockAlign, (int16_t *) module->outc->buf + i * d->samplesPerBlock); nframes += framesPerBlock; } d->track->nextfframe += nframes; if (blocksRead > 0) d->track->fpos_next_frame += blocksRead * d->blockAlign; assert(af_ftell(d->fh) == d->track->fpos_next_frame); /* If we got EOF from read, then we return the actual amount read. Complain only if there should have been more frames in the file. */ if (d->track->totalfframes != -1 && nframes != frames2read) { /* Report error if we haven't already */ if (d->track->filemodhappy) { _af_error(AF_BAD_READ, "file missing data -- read %d frames, should be %d", d->track->nextfframe, d->track->totalfframes); d->track->filemodhappy = AF_FALSE; } } module->outc->nframes = nframes; } static void ima_adpcm_reset1 (_AFmoduleinst *i) { ima_adpcm_data *d = (ima_adpcm_data *) i->modspec; AFframecount nextTrackFrame; int framesPerBlock; framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; nextTrackFrame = d->track->nextfframe; d->track->nextfframe = (nextTrackFrame / framesPerBlock) * framesPerBlock; d->framesToIgnore = nextTrackFrame - d->track->nextfframe; /* postroll = frames2ignore */ } static void ima_adpcm_reset2 (_AFmoduleinst *i) { ima_adpcm_data *d = (ima_adpcm_data *) i->modspec; int framesPerBlock; framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; d->track->fpos_next_frame = d->track->fpos_first_frame + d->blockAlign * (d->track->nextfframe / framesPerBlock); d->track->frames2ignore += d->framesToIgnore; assert(d->track->nextfframe % framesPerBlock == 0); } static _AFmodule ima_adpcm_decompress = { "ima_adpcm_decompress", ima_adpcm_decompress_describe, AF_NULL, AF_NULL, ima_adpcm_run_pull, ima_adpcm_reset1, ima_adpcm_reset2, AF_NULL, AF_NULL, AF_NULL, AF_NULL, _AFfreemodspec };