diff -r 719dade41745 decoders/wav.c --- a/decoders/wav.c Wed Aug 15 23:52:18 2012 -0400 +++ b/decoders/wav.c Fri Sep 12 06:50:35 2014 +0200 @@ -113,8 +113,9 @@ #define fmtID 0x20746D66 /* "fmt ", in ascii. */ -#define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */ -#define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */ +#define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */ +#define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */ +#define FMT_IMA 0x0011 /* IMA ADPCM compressed waveform data. */ typedef struct { @@ -130,6 +131,12 @@ Sint16 iSamp2; } ADPCMBLOCKHEADER; +typedef struct +{ + Sint16 iPrevSamp; + Uint8 iStepIndex; +} IMAADPCMDATA; + typedef struct S_WAV_FMT_T { Uint32 chunkID; @@ -166,6 +173,25 @@ Sint8 nibble; } adpcm; + struct + { + /* per channel decoder state */ + IMAADPCMDATA *d; + /* auxiliary buffer */ + Uint8 *buf; + + Uint16 block_frames; + Uint16 block_framesets; + Uint16 enc_frameset_size; + Uint16 headerset_size; + Uint16 dec_frame_size; + Uint16 dec_frameset_size; + Uint16 rem_block_framesets; + + /* whether the next word(s) are the start of a new block */ + int read_header; + } ima; + /* put other format-specific data here... */ } fmt; } fmt_t; @@ -614,6 +640,296 @@ /***************************************************************************** + * IMA ADPCM compression handler... * + *****************************************************************************/ + +static const int ima_index_table[16] = +{ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +static const int ima_step_table[89] = +{ + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +/* 1 frameset = 4 bytes (per channel) = 8 nibbles = 8 frames */ +#define FRAMESET_FRAMES 8 + + +static int read_ima_block_headers(SDL_RWops *rw, IMAADPCMDATA *d, + Uint16 chan_count, Sint16 *out) +{ + Uint16 i; + Uint8 dummy; + + for (i = 0; i < chan_count; i++) + { + BAIL_IF_MACRO(!read_le16(rw, &d[i].iPrevSamp), NULL, 0); + BAIL_IF_MACRO(!read_uint8(rw, &d[i].iStepIndex), NULL, 0); + BAIL_IF_MACRO(!read_uint8(rw, &dummy), NULL, 0); + + out[i] = d[i].iPrevSamp; + } /* for */ + + return(1); +} /* read_ima_block_headers */ + + +static Sint16 decode_ima_nibble(Uint8 nibble, IMAADPCMDATA *state) +{ + int step = ima_step_table[state->iStepIndex]; + int diff = 0; + int samp, index; + + if (nibble & 0x4) + diff += step >> 0; + if (nibble & 0x2) + diff += step >> 1; + if (nibble & 0x1) + diff += step >> 2; + + diff += step >> 3; + + if (nibble & 0x8) + diff = -diff; + + samp = state->iPrevSamp + diff; + samp = SDL_max(SDL_min(samp, 32767), -32768); + state->iPrevSamp = samp; + + index = state->iStepIndex + ima_index_table[nibble]; + state->iStepIndex = SDL_max(SDL_min(index, 88), 0); + + return samp; +} /* decode_ima_nibble */ + + +static int read_ima_frameset(SDL_RWops *rw, wav_t *wav, Sint16 *out) +{ + fmt_t *fmt = wav->fmt; + Uint16 i, j; + Uint8 *fs_buf = fmt->fmt.ima.buf; + Uint32 fs_buf_size = fmt->fmt.ima.enc_frameset_size; + Uint16 chan_count = fmt->wChannels; + IMAADPCMDATA *data = fmt->fmt.ima.d; + + /* read encoded frameset into temp buffer */ + BAIL_IF_MACRO(!SDL_RWread(rw, fs_buf, fs_buf_size, 1), NULL, 0); + + for (i = 0; i < chan_count; i++) + { + for (j = 0; j < 4; j++) + { + Uint8 byte = fs_buf[i*4+j]; + Sint16 *_out = out + j*chan_count*2+i; + + /* low nibble */ + *_out = decode_ima_nibble(byte & 0xF, &data[i]); + + _out += chan_count; + + /* high nibble */ + *_out = decode_ima_nibble(byte >> 4, &data[i]); + } + } /* for */ + + return(1); +} /* read_ima_frameset */ + + +static Uint32 read_sample_fmt_ima(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + void *const out_buf = internal->buffer; + Uint32 bw = 0; + int rc; + + while (1) + { + if (fmt->fmt.ima.read_header) + { + if (w->bytesLeft < fmt->fmt.ima.headerset_size) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + break; + } /* if */ + + if (bw+fmt->fmt.ima.dec_frame_size > internal->buffer_size) + break; + + rc = read_ima_block_headers(internal->rw, fmt->fmt.ima.d, + fmt->wChannels, (Sint16*)out_buf+bw); + + if (!rc) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return 0; + } /* if */ + + w->bytesLeft -= fmt->fmt.ima.headerset_size; + bw += fmt->fmt.ima.dec_frame_size; + + fmt->fmt.ima.read_header = 0; + fmt->fmt.ima.rem_block_framesets = fmt->fmt.ima.block_framesets; + } /* if */ + + if (w->bytesLeft < fmt->fmt.ima.enc_frameset_size) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + break; + } /* if */ + + if (bw+fmt->fmt.ima.dec_frameset_size > internal->buffer_size) + break; + + rc = read_ima_frameset(internal->rw, w, (Sint16*)out_buf+bw); + + if (!rc) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return 0; + } /* if */ + + bw += fmt->fmt.ima.dec_frameset_size; + w->bytesLeft -= fmt->fmt.ima.enc_frameset_size; + + if (--fmt->fmt.ima.rem_block_framesets == 0) + fmt->fmt.ima.read_header = 1; + } /* while */ + + return(bw); +} /* read_sample_fmt_ima */ + + +static void free_fmt_ima(fmt_t *fmt) +{ + free(fmt->fmt.ima.d); + free(fmt->fmt.ima.buf); +} /* free_fmt_ima */ + + +static int rewind_sample_fmt_ima(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + + fmt->fmt.ima.read_header = 1; + + return(1); +} /* rewind_sample_fmt_ima */ + + +static int seek_sample_fmt_ima(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + Sint16 *dummy_buf = (Sint16*) (fmt->fmt.ima.buf + + fmt->fmt.ima.enc_frameset_size); + Uint32 i; + int rc, pos; + + Uint32 seek_frames = (fmt->dwSamplesPerSec * ms) / 1000; + Uint32 seek_blocks = seek_frames / fmt->fmt.ima.block_frames; + Uint32 seek_framesets; + seek_frames %= fmt->fmt.ima.block_frames; + + w->bytesLeft = fmt->total_bytes; + pos = seek_blocks * fmt->wBlockAlign + fmt->data_starting_offset; + rc = SDL_RWseek(internal->rw, pos, SEEK_SET); + BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); + w->bytesLeft -= seek_blocks * fmt->wBlockAlign; + + fmt->fmt.ima.read_header = 0; + + if (seek_frames == 0) + { + fmt->fmt.ima.read_header = 1; + return(1); + } /* if */ + + rc = read_ima_block_headers(internal->rw, fmt->fmt.ima.d, + fmt->wChannels, dummy_buf); + BAIL_IF_MACRO(!rc, NULL, 0); + w->bytesLeft -= fmt->fmt.ima.headerset_size; + + if (w->bytesLeft < fmt->fmt.ima.headerset_size) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + return(1); + } /* if */ + + seek_frames -= 1; + seek_framesets = seek_frames / FRAMESET_FRAMES; + + for (i = 0; i < seek_framesets; i++) + { + rc = read_ima_frameset(internal->rw, w, dummy_buf); + BAIL_IF_MACRO(!rc, NULL, 0); + w->bytesLeft -= fmt->fmt.ima.enc_frameset_size; + } /* for */ + + fmt->fmt.ima.rem_block_framesets = + fmt->fmt.ima.block_framesets - seek_framesets; + + if (w->bytesLeft < fmt->fmt.ima.enc_frameset_size) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + + return(1); /* success. */ +} /* seek_sample_fmt_ima */ + + +static int read_fmt_ima(SDL_RWops *rw, fmt_t *fmt) +{ + Uint16 chan = fmt->wChannels; + Sint16 extraBytes; + int rc; + + /* setup function pointers */ + fmt->free = free_fmt_ima; + fmt->read_sample = read_sample_fmt_ima; + fmt->rewind_sample = rewind_sample_fmt_ima; + fmt->seek_sample = seek_sample_fmt_ima; + + BAIL_IF_MACRO(!read_le16(rw, &extraBytes), NULL, 0); + BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.ima.block_frames), NULL, 0); + + /* skip to end of fmt chunk */ + rc = SDL_RWseek(rw, extraBytes-2, SEEK_CUR); + BAIL_IF_MACRO(!rc, NULL, 0); + + fmt->fmt.ima.block_framesets = (fmt->fmt.ima.block_frames-1) / FRAMESET_FRAMES; + fmt->fmt.ima.enc_frameset_size = 4 * chan; + fmt->fmt.ima.headerset_size = 4 * chan; + fmt->fmt.ima.dec_frame_size = sizeof(Uint16) * chan; + fmt->fmt.ima.dec_frameset_size = fmt->fmt.ima.dec_frame_size * FRAMESET_FRAMES; + fmt->fmt.ima.read_header = 1; + + fmt->fmt.ima.d = malloc(sizeof(IMAADPCMDATA) * chan); + + size_t buf_size = fmt->fmt.ima.enc_frameset_size + + fmt->fmt.ima.dec_frameset_size; + fmt->fmt.ima.buf = malloc(buf_size); + + return(1); +} /* read_fmt_ima */ + + + +/***************************************************************************** * Everything else... * *****************************************************************************/ @@ -642,6 +958,13 @@ SNDDBG(("WAV: Appears to be ADPCM compressed audio.\n")); return(read_fmt_adpcm(rw, fmt)); + case FMT_IMA: + if (fmt->wBitsPerSample == 4) + { + SNDDBG(("WAV: Appears to be 4bit IMA ADPCM compressed audio.\n")); + return(read_fmt_ima(rw, fmt)); + } + /* add other types here. */ default: