diff options
Diffstat (limited to 'apps/codecs/wav.c')
-rw-r--r-- | apps/codecs/wav.c | 122 |
1 files changed, 110 insertions, 12 deletions
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c index a642f99a0f..293089a737 100644 --- a/apps/codecs/wav.c +++ b/apps/codecs/wav.c @@ -45,27 +45,37 @@ enum { WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Unknown Wave Format */ WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ + WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */ WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ + WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */ + WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */ + WAVE_FORMAT_XBOX_ADPCM = 0x0069, /* XBOX ADPCM */ IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ + WAVE_FORMAT_SWF_ADPCM = 0x5346, /* Adobe SWF ADPCM */ WAVE_FORMAT_EXTENSIBLE = 0xFFFE }; const struct pcm_entry wave_codecs[] = { { WAVE_FORMAT_UNKNOWN, 0 }, { WAVE_FORMAT_PCM, get_linear_pcm_codec }, + { WAVE_FORMAT_ADPCM, get_ms_adpcm_codec }, { WAVE_FORMAT_IEEE_FLOAT, get_ieee_float_codec }, { WAVE_FORMAT_ALAW, get_itut_g711_alaw_codec }, { WAVE_FORMAT_MULAW, get_itut_g711_mulaw_codec }, { WAVE_FORMAT_DVI_ADPCM, get_dvi_adpcm_codec }, + { WAVE_FORMAT_DIALOGIC_OKI_ADPCM, get_dialogic_oki_adpcm_codec }, + { WAVE_FORMAT_YAMAHA_ADPCM, get_yamaha_adpcm_codec }, + { WAVE_FORMAT_XBOX_ADPCM, get_dvi_adpcm_codec }, { IBM_FORMAT_MULAW, get_itut_g711_mulaw_codec }, { IBM_FORMAT_ALAW, get_itut_g711_alaw_codec }, + { WAVE_FORMAT_SWF_ADPCM, get_swf_adpcm_codec }, }; -#define NUM_FORMATS 8 +#define NUM_FORMATS 13 static const struct pcm_codec *get_wave_codec(uint32_t formattag) { @@ -83,13 +93,67 @@ static const struct pcm_codec *get_wave_codec(uint32_t formattag) return 0; } +static struct pcm_format format; +static uint32_t bytesdone; + +static bool set_msadpcm_coeffs(const uint8_t *buf) +{ + int i; + int num; + int size; + + buf += 4; /* skip 'fmt ' */ + size = buf[0] | (buf[1] << 8) | (buf[1] << 16) | (buf[1] << 24); + if (size < 50) + { + DEBUGF("CODEC_ERROR: microsoft adpcm 'fmt ' chunk size=%lu < 50\n", + (unsigned long)size); + return false; + } + + /* get nNumCoef */ + buf += 24; + num = buf[0] | (buf[1] << 8); + + /* + * In many case, nNumCoef is 7. + * Depending upon the encoder, as for this value there is a possibility of + * increasing more. + * If you found the file where this value exceeds 7, please report. + */ + if (num != MSADPCM_NUM_COEFF) + { + DEBUGF("CODEC_ERROR: microsoft adpcm nNumCoef=%d != 7\n", num); + return false; + } + + /* get aCoeffs */ + buf += 2; + for (i = 0; i < MSADPCM_NUM_COEFF; i++) + { + format.coeffs[i][0] = buf[0] | (SE(buf[1]) << 8); + format.coeffs[i][1] = buf[2] | (SE(buf[3]) << 8); + buf += 4; + } + + return true; +} + +static uint8_t *read_buffer(size_t *realsize) +{ + uint8_t *buffer = (uint8_t *)ci->request_buffer(realsize, format.chunksize); + if (bytesdone + (*realsize) > format.numbytes) + *realsize = format.numbytes - bytesdone; + bytesdone += *realsize; + ci->advance_buffer(*realsize); + return buffer; +} /* this is the codec entry point */ enum codec_status codec_main(void) { int status = CODEC_OK; - struct pcm_format format; - uint32_t bytesdone, decodedbytes; + uint32_t decodedsamples; uint32_t i; size_t n; int bufcount; @@ -125,6 +189,7 @@ next_track: goto done; } if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) { + DEBUGF("CODEC_ERROR: missing riff header\n"); status = CODEC_ERROR; goto done; } @@ -137,7 +202,7 @@ next_track: format.is_signed = true; format.is_little_endian = true; - decodedbytes = 0; + decodedsamples = 0; codec = 0; /* iterate over WAVE chunks until the 'data' chunk, which should be after the 'fmt ' chunk */ @@ -200,11 +265,21 @@ next_track: } } + /* msadpcm specific */ + if (format.formattag == WAVE_FORMAT_ADPCM) + { + if (!set_msadpcm_coeffs(buf)) + { + status = CODEC_ERROR; + goto done; + } + } + /* get codec */ codec = get_wave_codec(format.formattag); if (!codec) { - DEBUGF("CODEC_ERROR: unsupported wave format %x\n", + DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n", (unsigned int) format.formattag); status = CODEC_ERROR; goto done; @@ -215,7 +290,7 @@ next_track: format.is_signed = false; /* set format, parse codec specific tag, check format, and calculate chunk size */ - if (!codec->set_format(&format, buf)) + if (!codec->set_format(&format)) { status = CODEC_ERROR; goto done; @@ -256,12 +331,34 @@ next_track: status = CODEC_ERROR; goto done; } + if (format.samplesperblock == 0) { + DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); + status = CODEC_ERROR; + goto done; + } + if (format.blockalign == 0) + { + DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); + i = CODEC_ERROR; + goto done; + } if (format.numbytes == 0) { DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); status = CODEC_ERROR; goto done; } + /* check chunksize */ + if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels + > PCM_CHUNK_SIZE) + format.chunksize = (PCM_CHUNK_SIZE / format.blockalign) * format.blockalign; + if (format.chunksize == 0) + { + DEBUGF("CODEC_ERROR: chunksize is 0\n"); + i = CODEC_ERROR; + goto done; + } + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); if (format.channels == 2) { ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); @@ -295,13 +392,14 @@ next_track: } if (ci->seek_time) { - uint32_t newpos = codec->get_seek_pos(ci->seek_time); + struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, &read_buffer); - if (newpos > format.numbytes) + decodedsamples = newpos->samples; + if (newpos->pos > format.numbytes) break; - if (ci->seek_buffer(firstblockposn + newpos)) + if (ci->seek_buffer(firstblockposn + newpos->pos)) { - bytesdone = newpos; + bytesdone = newpos->pos; } ci->seek_complete(); } @@ -324,11 +422,11 @@ next_track: ci->pcmbuf_insert(samples, NULL, bufcount); ci->advance_buffer(n); bytesdone += n; - decodedbytes += bufcount; + decodedsamples += bufcount; if (bytesdone >= format.numbytes) endofstream = 1; - ci->set_elapsed(decodedbytes*1000LL/ci->id3->frequency); + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); } status = CODEC_OK; |