diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c old mode 100644 new mode 100755 index 2e10d1e..66f1770 --- a/apps/codecs/aiff.c +++ b/apps/codecs/aiff.c @@ -70,7 +70,7 @@ enum codec_status codec_main(void) { int status = CODEC_OK; struct pcm_format format; - uint32_t bytesdone, decodedbytes; + uint32_t bytesdone, decodedsamples; uint32_t num_sample_frames = 0; uint32_t i = CODEC_OK; size_t n; @@ -128,7 +128,7 @@ next_track: format.is_signed = true; format.is_little_endian = false; - decodedbytes = 0; + decodedsamples = 0; codec = 0; /* read until 'SSND' chunk, which typically is last */ @@ -168,7 +168,8 @@ next_track: * aiff's sample_size is uncompressed sound data size. * But format.bitspersample is compressed sound data size. */ - if (format.formattag == AIFC_FORMAT_ALAW || format.formattag == AIFC_FORMAT_MULAW) + if (format.formattag == AIFC_FORMAT_ALAW || + format.formattag == AIFC_FORMAT_MULAW) format.bitspersample = 8; } else @@ -184,9 +185,9 @@ next_track: /* offset2snd */ offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11]; /* block_size */ - format.blockalign = (buf[12]<<24)|(buf[13]<<16)|(buf[14]<<8)|buf[15]; + format.blockalign = ((buf[12]<<24)|(buf[13]<<16)|(buf[14]<<8)|buf[15]) >> 3; if (format.blockalign == 0) - format.blockalign = format.channels*format.bitspersample; + format.blockalign = format.channels * format.bitspersample >> 3; format.numbytes = i - 8 - offset2snd; i = 8 + offset2snd; /* advance to the beginning of data */ } else if (is_aifc && (memcmp(buf, "FVER", 4)==0)) { @@ -228,7 +229,7 @@ next_track: goto done; } - if (!codec->set_format(&format, 0)) + if (!codec->set_format(&format)) { i = CODEC_ERROR; goto done; @@ -246,6 +247,30 @@ next_track: goto done; } + if (format.samplesperblock == 0) + { + DEBUGF("CODEC_ERROR: samplesperblock is 0\n"); + i = CODEC_ERROR; + goto done; + } + if (format.blockalign == 0) + { + DEBUGF("CODEC_ERROR: blockalign is 0\n"); + i = 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; + } + firstblockposn = 1024 - n; ci->advance_buffer(firstblockposn); @@ -260,11 +285,14 @@ next_track: break; if (ci->seek_time) { - uint32_t newpos = codec->get_seek_pos(ci->seek_time); - if (newpos > format.numbytes) + /* 2nd args(read_buffer) is unnecessary in the format which AIFF supports. */ + struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, NULL); + + decodedsamples = newpos->samples; + if (newpos->pos > format.numbytes) break; - if (ci->seek_buffer(firstblockposn + newpos)) - bytesdone = newpos; + if (ci->seek_buffer(firstblockposn + newpos->pos)) + bytesdone = newpos->pos; ci->seek_complete(); } aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); @@ -288,11 +316,11 @@ next_track: 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); } i = CODEC_OK; diff --git a/apps/codecs/libpcm/SOURCES b/apps/codecs/libpcm/SOURCES index 89be345..1352ce7 100644 --- a/apps/codecs/libpcm/SOURCES +++ b/apps/codecs/libpcm/SOURCES @@ -2,3 +2,7 @@ linear_pcm.c itut_g711.c dvi_adpcm.c ieee_float.c +adpcm_seek.c +dialogic_oki_adpcm.c +ms_adpcm.c +yamaha_adpcm.c diff --git a/apps/codecs/libpcm/dvi_adpcm.c b/apps/codecs/libpcm/dvi_adpcm.c index 3df5e90..00dabb2 100644 --- a/apps/codecs/libpcm/dvi_adpcm.c +++ b/apps/codecs/libpcm/dvi_adpcm.c @@ -48,12 +48,10 @@ static const int dvi_adpcm_indextab3[4] ICONST_ATTR = { -1, -1, 1, 2 }; static struct pcm_format *fmt; -static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) +static bool set_format(struct pcm_format *format) { fmt = format; - (void)fmtpos; - if (fmt->bitspersample != 4 && fmt->bitspersample != 3) { DEBUGF("CODEC_ERROR: dvi_adpcm must have 3 or 4 bitspersample\n"); @@ -65,31 +63,22 @@ static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) return false; } - /* chunksize is computed so that one chunk is about 1/50s. - * this make 4096 for 44.1kHz 16bits stereo. - * It also has to be a multiple of blockalign */ - fmt->chunksize = (1 + fmt->avgbytespersec / (50*fmt->blockalign))*fmt->blockalign; - - /* check that the output buffer is big enough (convert to samplespersec, - then round to the blockalign multiple below) */ - if ((((uint64_t)fmt->chunksize * ci->id3->frequency * fmt->channels * fmt->bitspersample)>>3) - /(uint64_t)fmt->avgbytespersec >= PCM_CHUNK_SIZE) - fmt->chunksize = ((uint64_t)PCM_CHUNK_SIZE * fmt->avgbytespersec - /((uint64_t)ci->id3->frequency * fmt->channels * 2 - * fmt->blockalign)) * fmt->blockalign; + fmt->chunksize = fmt->blockalign; return true; } -static uint32_t get_seek_pos(long seek_time) +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) { - uint32_t newpos; + static struct pcm_pos newpos; + uint32_t newblock = ((uint64_t)seek_time * ci->id3->frequency) + / (1000LL * fmt->samplesperblock); - /* use avgbytespersec to round to the closest blockalign multiple, - add firstblockposn. 64-bit casts to avoid overflows. */ - newpos = (((uint64_t)fmt->avgbytespersec*(seek_time - 1)) - / (1000LL*fmt->blockalign))*fmt->blockalign; - return newpos; + (void)read_buffer; + newpos.pos = newblock * fmt->blockalign; + newpos.samples = newblock * fmt->samplesperblock; + return &newpos; } static int decode_dvi_adpcm(const uint8_t *inbuf, size_t inbufsize, diff --git a/apps/codecs/libpcm/ieee_float.c b/apps/codecs/libpcm/ieee_float.c index c0e91a4..0530993 100644 --- a/apps/codecs/libpcm/ieee_float.c +++ b/apps/codecs/libpcm/ieee_float.c @@ -28,33 +28,38 @@ static struct pcm_format *fmt; -static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) +static bool set_format(struct pcm_format *format) { fmt = format; - (void)fmtpos; - if (fmt->bitspersample != 32 && fmt->bitspersample != 64) { - DEBUGF("CODEC_ERROR: ieee float must be 32 or 64 bitspersample %d\n", fmt->bitspersample); + DEBUGF("CODEC_ERROR: ieee float must be 32 or 64 bitspersample: %d\n", + fmt->bitspersample); return false; } fmt->bytespersample = fmt->bitspersample >> 3; - fmt->blockalign = fmt->bytespersample; + fmt->samplesperblock = fmt->blockalign / (fmt->bytespersample * fmt->channels); - /* chunksize is computed so that one chunk is about 1/50s. */ - fmt->chunksize = (ci->id3->frequency * fmt->channels / 50) * fmt->blockalign; + /* chunksize = about 1/50[sec] data */ + fmt->chunksize = (ci->id3->frequency / (50 * fmt->samplesperblock)) + * fmt->blockalign; return true; } -static uint32_t get_seek_pos(long seek_time) +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) { - uint32_t newpos; - - newpos = ((uint64_t)(seek_time * ci->id3->frequency * fmt->channels / 1000LL))*fmt->blockalign; - return newpos; + static struct pcm_pos newpos; + uint32_t newblock = ((uint64_t)seek_time * ci->id3->frequency) + / (1000LL * fmt->samplesperblock); + + (void)read_buffer; + newpos.pos = newblock * fmt->blockalign; + newpos.samples = newblock * fmt->samplesperblock; + return &newpos; } static int decode(const uint8_t *inbuf, size_t inbufsize, diff --git a/apps/codecs/libpcm/itut_g711.c b/apps/codecs/libpcm/itut_g711.c index 520ca46..9a38031 100644 --- a/apps/codecs/libpcm/itut_g711.c +++ b/apps/codecs/libpcm/itut_g711.c @@ -109,49 +109,43 @@ static const int16_t ulaw2linear16[256] ICONST_ATTR = { static struct pcm_format *fmt; -static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) +static bool set_format(struct pcm_format *format) { fmt = format; - (void)fmtpos; - if (fmt->bitspersample != 8) { - DEBUGF("CODEC_ERROR: alaw and mulaw must have 8 bitspersample\n"); + DEBUGF("CODEC_ERROR: alaw and mulaw must have 8 bitspersample: %d\n", + fmt->bitspersample); return false; } if (fmt->totalsamples == 0) { - fmt->bytespersample = fmt->channels; - fmt->totalsamples = fmt->numbytes/fmt->bytespersample; + fmt->bytespersample = 1; + fmt->totalsamples = fmt->numbytes / (fmt->bytespersample * fmt->channels); } - /* chunksize is computed so that one chunk is about 1/50s. - * this make 4096 for 44.1kHz 16bits stereo. - * It also has to be a multiple of blockalign */ - fmt->chunksize = (1 + fmt->avgbytespersec / (50*fmt->blockalign))*fmt->blockalign; + fmt->samplesperblock = fmt->blockalign / (fmt->bytespersample * fmt->channels); - /* check that the output buffer is big enough (convert to samplespersec, - then round to the blockalign multiple below) */ - if ((((uint64_t)fmt->chunksize * ci->id3->frequency * fmt->channels * fmt->bitspersample)>>3) - /(uint64_t)fmt->avgbytespersec >= PCM_CHUNK_SIZE) - fmt->chunksize = ((uint64_t)PCM_CHUNK_SIZE * fmt->avgbytespersec - /((uint64_t)ci->id3->frequency * fmt->channels * 2 - * fmt->blockalign)) * fmt->blockalign; + /* chunksize = about 1/50[sec] data */ + fmt->chunksize = (ci->id3->frequency / (50 * fmt->samplesperblock)) + * fmt->blockalign; return true; } -static uint32_t get_seek_pos(long seek_time) +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) { - uint32_t newpos; - - /* use avgbytespersec to round to the closest blockalign multiple, - add firstblockposn. 64-bit casts to avoid overflows. */ - newpos = (((uint64_t)fmt->avgbytespersec*(seek_time - 1)) - / (1000LL*fmt->blockalign))*fmt->blockalign; - return newpos; + static struct pcm_pos newpos; + uint32_t newblock = ((uint64_t)seek_time * ci->id3->frequency) + / (1000LL * fmt->samplesperblock); + + (void)read_buffer; + newpos.pos = newblock * fmt->blockalign; + newpos.samples = newblock * fmt->samplesperblock; + return &newpos; } static int decode_alaw(const uint8_t *inbuf, size_t inbufsize, diff --git a/apps/codecs/libpcm/linear_pcm.c b/apps/codecs/libpcm/linear_pcm.c index 4db27ca..5360d79 100644 --- a/apps/codecs/libpcm/linear_pcm.c +++ b/apps/codecs/libpcm/linear_pcm.c @@ -29,12 +29,10 @@ static struct pcm_format *fmt; -static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) +static bool set_format(struct pcm_format *format) { fmt = format; - (void)fmtpos; - if (fmt->bitspersample > 32) { DEBUGF("CODEC_ERROR: pcm with more than 32 bitspersample " @@ -42,33 +40,34 @@ static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) return false; } + fmt->bytespersample = fmt->bitspersample >> 3; + if (fmt->totalsamples == 0) - { - fmt->bytespersample = (((fmt->bitspersample - 1)/8 + 1)*fmt->channels); fmt->totalsamples = fmt->numbytes/fmt->bytespersample; - } - /* chunksize is computed so that one chunk is about 1/50s. - * this make 4096 for 44.1kHz 16bits stereo. - * It also has to be a multiple of blockalign */ - fmt->chunksize = (1 + fmt->avgbytespersec / (50*fmt->blockalign))*fmt->blockalign; + fmt->samplesperblock = fmt->blockalign / (fmt->bytespersample * fmt->channels); + + /* chunksize = about 1/50[sec] data */ + fmt->chunksize = (ci->id3->frequency / (50 * fmt->samplesperblock)) + * fmt->blockalign; return true; } -static uint32_t get_seek_pos(long seek_time) +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) { - uint32_t newpos; - - /* use avgbytespersec to round to the closest blockalign multiple, - add firstblockposn. 64-bit casts to avoid overflows. */ - newpos = (((uint64_t)fmt->avgbytespersec*(seek_time - 1)) - / (1000LL*fmt->blockalign))*fmt->blockalign; - return newpos; + static struct pcm_pos newpos; + uint32_t newblock = ((uint64_t)seek_time * ci->id3->frequency) + / (1000LL * fmt->samplesperblock); + + (void)read_buffer; + newpos.pos = newblock * fmt->blockalign; + newpos.samples = newblock * fmt->samplesperblock; + return &newpos; } -static int decode(const uint8_t *inbuf, size_t inbufsize, - int32_t *outbuf, int *outbufsize) +static int decode(const uint8_t *inbuf, size_t inbufsize, int32_t *outbuf, int *outbufsize) { uint32_t i; diff --git a/apps/codecs/libpcm/pcm_common.h b/apps/codecs/libpcm/pcm_common.h old mode 100644 new mode 100755 index 757d0ad..d490a85 --- a/apps/codecs/libpcm/pcm_common.h +++ b/apps/codecs/libpcm/pcm_common.h @@ -18,8 +18,8 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef CODEC_LIBPCMS_PCM_COMMON_H -#define CODEC_LIBPCMS_PCM_COMMON_H +#ifndef CODEC_LIBPCM_PCM_COMMON_H +#define CODEC_LIBPCM_PCM_COMMON_H #include #include @@ -38,6 +38,19 @@ /* Macro that shift to -0x80. (0 .. 127 to -128 .. -1, 128 .. 255 to 0 .. 127) */ #define SFT(x) ((int32_t)x-0x80) +/* Macro that clipping data */ +#define CLIP(data, min, max) \ +if ((data) > (max)) data = max; \ +else if ((data) < (min)) data = min; + +/* nums of msadpcm coeffs + * 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. + */ +#define MSADPCM_NUM_COEFF 7 + struct pcm_format { /* * RIFF: wFormatTag (in 'fmt ' chunk) @@ -103,13 +116,67 @@ struct pcm_format { * true: signed, false: unsigned */ bool is_signed; + + /* the following values are format speciffic parameters */ + + /* microsoft adpcm: aCoeff */ + int16_t coeffs[MSADPCM_NUM_COEFF][2]; +}; + +struct pcm_pos { + uint32_t pos; + uint32_t samples; }; struct pcm_codec { - bool (*set_format)(struct pcm_format *format, const unsigned char *fmtpos); - uint32_t (*get_seek_pos)(long seek_time); + /* + * sets the format speciffic RIFF/AIFF header information and checks the pcm_format. + * + * [In/Out] format + * the structure which supplies RIFF/AIFF header information. + * + * return + * true: RIFF/AIFF header check OK + * false: RIFF/AIFF header check NG + */ + bool (*set_format)(struct pcm_format *format); + + /* + * get seek position + * + * [In] seek_time + * seek time [ms] + * + * [In] read_buffer + * the function which reads the data from the file (chunksize bytes read). + * + * return + * position after the seeking. + */ + struct pcm_pos *(*get_seek_pos)(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)); + + /* + * decode wave data. + * + * [In] inbuf + * the start pointer of wave data buffer. + * + * [In] inbufsize + * wave data buffer size (bytes). + * + * [Out] outbuf + * the start pointer of the buffer which supplies decoded pcm data. + * + * [Out] outbufcount + * decoded pcm data count. + * + * return + * CODEC_OK: decode succeed. + * CODEC_ERROR: decode failure. + */ int (*decode)(const uint8_t *inbuf, size_t inbufsize, - int32_t *outbuf, int *outbufsize); + int32_t *outbuf, int *outbufcount); }; struct pcm_entry { diff --git a/apps/codecs/libpcm/support_formats.h b/apps/codecs/libpcm/support_formats.h index 0a6ea33..fb1617e 100644 --- a/apps/codecs/libpcm/support_formats.h +++ b/apps/codecs/libpcm/support_formats.h @@ -18,8 +18,8 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef CODEC_LIBPCMS_SUPPORT_FORMATS_H -#define CODEC_LIBPCMS_SUPPORT_FORMATS_H +#ifndef CODEC_LIBPCM_SUPPORT_FORMATS_H +#define CODEC_LIBPCM_SUPPORT_FORMATS_H #include "pcm_common.h" @@ -37,4 +37,13 @@ const struct pcm_codec *get_dvi_adpcm_codec(void); /* IEEE float */ const struct pcm_codec *get_ieee_float_codec(void); + +/* Microsoft ADPCM */ +const struct pcm_codec *get_ms_adpcm_codec(void); + +/* Dialogic OKI ADPCM */ +const struct pcm_codec *get_dialogic_oki_adpcm_codec(void); + +/* YAMAHA ADPCM */ +const struct pcm_codec *get_yamaha_adpcm_codec(void); #endif diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c old mode 100644 new mode 100755 index a642f99..664f993 --- a/apps/codecs/wav.c +++ b/apps/codecs/wav.c @@ -45,10 +45,13 @@ 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 */ IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ WAVE_FORMAT_EXTENSIBLE = 0xFFFE @@ -57,15 +60,18 @@ enum 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 }, { IBM_FORMAT_MULAW, get_itut_g711_mulaw_codec }, { IBM_FORMAT_ALAW, get_itut_g711_alaw_codec }, }; -#define NUM_FORMATS 8 +#define NUM_FORMATS 11 static const struct pcm_codec *get_wave_codec(uint32_t formattag) { @@ -83,13 +89,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 +185,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 +198,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 +261,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 +286,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 +327,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 +388,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 +418,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; diff --git a/apps/metadata/wave.c b/apps/metadata/wave.c old mode 100644 new mode 100755 index acef32d..c8a0b19 --- a/apps/metadata/wave.c +++ b/apps/metadata/wave.c @@ -28,6 +28,7 @@ #include "metadata.h" #include "metadata_common.h" #include "metadata_parsers.h" +#include "logf.h" # define AV_WL32(p, d) do { \ ((uint8_t*)(p))[0] = (d); \ @@ -40,29 +41,91 @@ ((uint8_t*)(p))[1] = (d)>>8; \ } while(0) +enum +{ + 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 */ + IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ + IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ + WAVE_FORMAT_ATRAC3 = 0x0270, /* Atrac3 stream */ +}; + +struct wave_fmt { + unsigned int formattag; + unsigned long channels; + unsigned int blockalign; + unsigned long bitspersample; + unsigned int samplesperblock; + unsigned long numbytes; +}; + +static unsigned long get_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3) +{ + unsigned long totalsamples = 0; + + switch (fmt->formattag) + { + case WAVE_FORMAT_PCM: + case WAVE_FORMAT_IEEE_FLOAT: + case WAVE_FORMAT_ALAW: + case WAVE_FORMAT_MULAW: + case IBM_FORMAT_ALAW: + case IBM_FORMAT_MULAW: + totalsamples = + fmt->numbytes / ((((fmt->bitspersample - 1) / 8) + 1) * fmt->channels); + break; + case WAVE_FORMAT_ADPCM: + case WAVE_FORMAT_DVI_ADPCM: + totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock; + break; + case WAVE_FORMAT_YAMAHA_ADPCM: + if (fmt->samplesperblock == 0) + { + if (fmt->blockalign == ((id3->frequency / 60) + 4) * fmt->channels) + fmt->samplesperblock = id3->frequency / 30; + else + fmt->samplesperblock = fmt->blockalign * 2 / fmt->channels; + } + totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock; + break; + case WAVE_FORMAT_DIALOGIC_OKI_ADPCM: + totalsamples = 2 * fmt->numbytes; + break; + default: + totalsamples = 0; + break; + } + return totalsamples; +} + bool get_wave_metadata(int fd, struct mp3entry* id3) { /* Use the trackname part of the id3 structure as a temporary buffer */ unsigned char* buf = (unsigned char *)id3->path; + struct wave_fmt fmt; unsigned long totalsamples = 0; - unsigned long channels = 0; - unsigned long bitspersample = 0; - unsigned long numbytes = 0; unsigned long offset = 0; int read_bytes; int i; + memset(&fmt, 0, sizeof(struct wave_fmt)); + /* get RIFF chunk header */ - if ((lseek(fd, 0, SEEK_SET) < 0) - || ((read_bytes = read(fd, buf, 12)) < 12)) + if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 12) < 12)) { return false; } offset += 12; - if ((memcmp(buf, "RIFF",4) != 0) - || (memcmp(&buf[8], "WAVE", 4) !=0 )) + if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) { + DEBUGF("metadata error: missing riff header.\n"); return false; } @@ -70,7 +133,7 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) while (true) { /* get chunk header */ - if ((read_bytes = read(fd, buf, 8)) < 8) + if (read(fd, buf, 8) < 8) return false; offset += 8; @@ -80,26 +143,41 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) if (memcmp(buf, "fmt ", 4) == 0) { /* get rest of chunk */ - if ((read_bytes = read(fd, buf, 16)) < 16) + if (i < 16) return false; - offset += 16; - i -= 16; + read_bytes = 16; + if (i > 19) + read_bytes = 20; + + if (read(fd, buf, read_bytes) != read_bytes) + return false; - /* skipping wFormatTag */ + offset += read_bytes; + i -= read_bytes; + + /* wFormatTag */ + fmt.formattag = buf[0] | (buf[1] << 8); /* wChannels */ - channels = buf[2] | (buf[3] << 8); + fmt.channels = buf[2] | (buf[3] << 8); /* dwSamplesPerSec */ id3->frequency = get_long_le(&buf[4]); /* dwAvgBytesPerSec */ id3->bitrate = (get_long_le(&buf[8]) * 8) / 1000; /* wBlockAlign */ - id3->bytesperframe = buf[12] | (buf[13] << 8); + fmt.blockalign = buf[12] | (buf[13] << 8); + id3->bytesperframe = fmt.blockalign; /* wBitsPerSample */ - bitspersample = buf[14] | (buf[15] << 8); + fmt.bitspersample = buf[14] | (buf[15] << 8); + if (read_bytes > 19) + { + /* wSamplesPerBlock */ + fmt.samplesperblock = buf[18] | (buf[19] << 8); + } + /* Check for ATRAC3 stream */ - if((buf[0] | (buf[1] << 8)) == 0x0270) - { + if (fmt.formattag == WAVE_FORMAT_ATRAC3) + { int jsflag = 0; if(id3->bitrate == 66 || id3->bitrate == 94) jsflag = 1; @@ -107,7 +185,7 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) id3->extradata_size = 14; id3->channels = 2; id3->codectype = AFMT_OMA_ATRAC3; - /* Store the extradata for the codec */ + /* Store the extradata for the codec */ AV_WL16(&id3->id3v2buf[0], 1); // always 1 AV_WL32(&id3->id3v2buf[2], id3->frequency); // samples rate AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode @@ -118,8 +196,9 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) } else if (memcmp(buf, "data", 4) == 0) { - numbytes = i; - id3->first_frame_offset = offset; + fmt.numbytes = i; + if (fmt.formattag == WAVE_FORMAT_ATRAC3) + id3->first_frame_offset = offset; break; } else if (memcmp(buf, "fact", 4) == 0) @@ -128,7 +207,7 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) if (i >= 4) { /* get rest of chunk */ - if ((read_bytes = read(fd, buf, 4)) < 4) + if (read(fd, buf, 4) < 4) return false; offset += 4; i -= 4; @@ -145,16 +224,15 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) offset += i; } - if ((numbytes == 0) || (channels == 0)) + if ((fmt.numbytes == 0) || (fmt.channels == 0) || (fmt.blockalign == 0)) { + DEBUGF("metadata error: numbytes, channels, or blockalign is 0.\n"); return false; } if (totalsamples == 0) { - /* for PCM only */ - totalsamples = numbytes - / ((((bitspersample - 1) / 8) + 1) * channels); + totalsamples = get_totalsamples(&fmt, id3); } id3->vbr = false; /* All WAV files are CBR */ diff --git a/apps/codecs/libpcm/adpcm_seek.c b/apps/codecs/libpcm/adpcm_seek.c new file mode 100644 index 0000000..ce49d5f --- /dev/null +++ b/apps/codecs/libpcm/adpcm_seek.c @@ -0,0 +1,101 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "adpcm_seek.h" +#include "codeclib.h" + +/* + * The helper functions in order to seek for the adpcm codec + * which does not include the header each the data block. + */ + +#define MAX_STORE_COUNT 1000 + +static struct adpcm_data seek_table[MAX_STORE_COUNT]; +static int seek_count; +static int cur_count; +static int max_ratio; +static int cur_ratio; + +void init_seek_table(uint32_t max_count) +{ + int i = 0; + + for ( ; i < MAX_STORE_COUNT; i++) + { + seek_table[i].is_valid = false; + } + seek_count = max_count / MAX_STORE_COUNT + 1; + max_ratio = max_count / seek_count + 1; + cur_count = 0; + cur_ratio = -1; +} + +void add_adpcm_data(struct adpcm_data *data) +{ + if (--cur_count <= 0) + { + cur_count = seek_count; + if (++cur_ratio >= max_ratio) + cur_ratio = max_ratio - 1; + + if (!seek_table[cur_ratio].is_valid) + { + seek_table[cur_ratio].pcmdata[0] = data->pcmdata[0]; + seek_table[cur_ratio].pcmdata[1] = data->pcmdata[1]; + seek_table[cur_ratio].step[0] = data->step[0]; + seek_table[cur_ratio].step[1] = data->step[1]; + seek_table[cur_ratio].is_valid = true; + } + } +} + +uint32_t seek(uint32_t count, struct adpcm_data *seek_data, + uint8_t *(*read_buffer)(size_t *realsize), + int (*decode)(const uint8_t *inbuf, size_t inbufsize)) +{ + int new_ratio = count / seek_count; + + if (new_ratio >= max_ratio) + new_ratio = max_ratio - 1; + + if (!seek_table[new_ratio].is_valid) + { + uint8_t *buffer; + size_t n; + + do + { + buffer = read_buffer(&n); + if (n == 0) + break; + decode(buffer, n); + } while (cur_ratio < new_ratio); + } + + seek_data->pcmdata[0] = seek_table[new_ratio].pcmdata[0]; + seek_data->pcmdata[1] = seek_table[new_ratio].pcmdata[1]; + seek_data->step[0] = seek_table[new_ratio].step[0]; + seek_data->step[1] = seek_table[new_ratio].step[1]; + + cur_ratio = new_ratio; + cur_count = seek_count; + return cur_ratio * seek_count; +} diff --git a/apps/codecs/libpcm/adpcm_seek.h b/apps/codecs/libpcm/adpcm_seek.h new file mode 100644 index 0000000..66ec390 --- /dev/null +++ b/apps/codecs/libpcm/adpcm_seek.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef CODEC_LIBPCM_ADPCM_SEEK_H +#define CODEC_LIBPCM_ADPCM_SEEK_H + +#include +#include +#include + +struct adpcm_data { + int16_t pcmdata[2]; + uint16_t step[2]; + bool is_valid; +}; + +void init_seek_table(uint32_t max_count); +void add_adpcm_data(struct adpcm_data *data); +uint32_t seek(uint32_t seek_time, struct adpcm_data *seek_data, + uint8_t *(*read_buffer)(size_t *realsize), + int (*decode)(const uint8_t *inbuf, size_t inbufsize)); +#endif diff --git a/apps/codecs/libpcm/dialogic_oki_adpcm.c b/apps/codecs/libpcm/dialogic_oki_adpcm.c new file mode 100644 index 0000000..e129300 --- /dev/null +++ b/apps/codecs/libpcm/dialogic_oki_adpcm.c @@ -0,0 +1,183 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "codeclib.h" +#include "pcm_common.h" +#include "adpcm_seek.h" + +/* + * Dialogic OKI ADPCM + * + * References + * [1] Dialogic Corporation, Dialogic ADPCM Algorithm, 1988 + * [2] MultimediaWiki, Dialogic IMA ADPCM, URL:http://wiki.multimedia.cx/index.php?title=Dialogic_IMA_ADPCM + * [3] sox source code, src/adpcms.c + * [4] Tetsuya Isaki, NetBSD:/sys/dev/audio.c, http://www.tri-tree.gr.jp/~isaki/NetBSD/src/sys/dev/ic/msm6258.c.html + */ + +static const uint16_t step_table[] ICONST_ATTR = { + 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, +}; + +static const int index_table[] ICONST_ATTR = { + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +static struct adpcm_data cur_data; + +static struct pcm_format *fmt; + +static bool set_format(struct pcm_format *format) +{ + uint32_t max_chunk_count; + + fmt = format; + + if (fmt->bitspersample != 4) + { + DEBUGF("CODEC_ERROR: dialogic oki adpcm must be 4 bitspersample: %d\n", + fmt->bitspersample); + return false; + } + + if (fmt->channels != 1) + { + DEBUGF("CODEC_ERROR: dialogic oki adpcm must be monaural\n"); + return false; + } + + /* blockalign = 2 samples */ + fmt->blockalign = 1; + fmt->samplesperblock = 2; + + /* chunksize = about 1/32[sec] data */ + fmt->chunksize = ci->id3->frequency >> 6; + + max_chunk_count = (uint64_t)ci->id3->length * ci->id3->frequency + / (2000LL * fmt->chunksize); + + /* initialize seek table */ + init_seek_table(max_chunk_count); + /* add first data */ + add_adpcm_data(&cur_data); + + return true; +} + +static int16_t create_pcmdata(uint8_t nibble) +{ + int16_t delta; + int16_t index = cur_data.step[0]; + int16_t step = step_table[index]; + + delta = (step >> 3); + if (nibble & 4) delta += step; + if (nibble & 2) delta += (step >> 1); + if (nibble & 1) delta += (step >> 2); + + if (nibble & 0x08) + cur_data.pcmdata[0] -= delta; + else + cur_data.pcmdata[0] += delta; + + CLIP(cur_data.pcmdata[0], -2048, 2047); + + index += index_table[nibble & 0x07]; + CLIP(index, 0, 48); + cur_data.step[0] = index; + + return cur_data.pcmdata[0]; +} + +static int decode(const uint8_t *inbuf, size_t inbufsize, + int32_t *outbuf, int *outbufcount) +{ + size_t nsamples = 0; + + while (inbufsize) + { + *outbuf++ = create_pcmdata(*inbuf >> 4) << 17; + *outbuf++ = create_pcmdata(*inbuf ) << 17; + nsamples += 2; + + inbuf++; + inbufsize--; + } + + *outbufcount = nsamples; + add_adpcm_data(&cur_data); + + return CODEC_OK; +} + +static int decode_for_seek(const uint8_t *inbuf, size_t inbufsize) +{ + while (inbufsize) + { + create_pcmdata(*inbuf >> 4); + create_pcmdata(*inbuf ); + + inbuf++; + inbufsize--; + } + + add_adpcm_data(&cur_data); + + return CODEC_OK; +} + +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) +{ + static struct pcm_pos newpos; + uint32_t seek_count = 0; + uint32_t new_count; + + if (seek_time > 0) + seek_count = (uint64_t)seek_time * ci->id3->frequency + / (2000LL * fmt->chunksize); + + new_count = seek(seek_count, &cur_data, read_buffer, &decode_for_seek); + newpos.pos = new_count * fmt->chunksize; + newpos.samples = (newpos.pos / fmt->blockalign) * fmt->samplesperblock; + return &newpos; +} + +static const struct pcm_codec codec = { + set_format, + get_seek_pos, + decode, + }; + +const struct pcm_codec *get_dialogic_oki_adpcm_codec(void) +{ + /* + * initialize first pcm data, step index + * because the dialogic oki adpcm is always monaural, + * pcmdata[1], step[1] do not use. + */ + cur_data.pcmdata[0] = 0; + cur_data.step[0] = 0; + + return &codec; +} diff --git a/apps/codecs/libpcm/ms_adpcm.c b/apps/codecs/libpcm/ms_adpcm.c new file mode 100644 index 0000000..899ecc2 --- /dev/null +++ b/apps/codecs/libpcm/ms_adpcm.c @@ -0,0 +1,166 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "codeclib.h" +#include "pcm_common.h" + +/* + * Microsoft ADPCM + * + * References + * [1] Microsoft, New Multimedia Data Types and Data Techniques Revision 3.0, 1994 + * [2] MulitimediaWiki, Microsoft ADPCM, 2006 + * (http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM) + * [3] ffmpeg source code, libavcodec/adpcm.c + */ + +#define ADPCM_NUM_COEFF 7 + +static int16_t dec_coeff[2][2]; +static uint16_t delta[2]; +static int16_t sample[2][2]; + +static struct pcm_format *fmt; + +static const int16_t adaptation_table[] ICONST_ATTR = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +static bool set_format(struct pcm_format *format) +{ + fmt = format; + + if (fmt->bitspersample != 4) + { + DEBUGF("CODEC_ERROR: microsoft adpcm must be 4 bitspersample: %d\n", + fmt->bitspersample); + return false; + } + + fmt->chunksize = fmt->blockalign; + + return true; +} + +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) +{ + static struct pcm_pos newpos; + uint32_t newblock = ((uint64_t)seek_time * ci->id3->frequency) + / (1000LL * fmt->samplesperblock); + + (void)read_buffer; + newpos.pos = newblock * fmt->blockalign; + newpos.samples = newblock * fmt->samplesperblock; + return &newpos; +} + +static int16_t create_pcmdata(int ch, uint8_t nibble) +{ + int32_t pcmdata; + + pcmdata = (sample[ch][0] * dec_coeff[ch][0] + + sample[ch][1] * dec_coeff[ch][1]) / 256; + pcmdata += (delta[ch] * (nibble - ((nibble & 0x8) << 1))); + + CLIP(pcmdata, -32768, 32767); + + sample[ch][1] = sample[ch][0]; + sample[ch][0] = pcmdata; + + delta[ch] = (adaptation_table[nibble] * delta[ch]) >> 8; + if (delta[ch] < 16) + delta[ch] = 16; + + return (int16_t)pcmdata; +} + +static int decode(const uint8_t *inbuf, size_t inbufsize, + int32_t *outbuf, int *outbufcount) +{ + int ch; + size_t nsamples = 0; + int size = fmt->samplesperblock; + + /* read block header */ + for (ch = 0; ch < fmt->channels; ch++) + { + if (*inbuf >= ADPCM_NUM_COEFF) + { + DEBUGF("CODEC_ERROR: microsoft adpcm illegal initial coeff=%d > 7\n", + *inbuf); + return CODEC_ERROR; + } + dec_coeff[ch][0] = fmt->coeffs[*inbuf][0]; + dec_coeff[ch][1] = fmt->coeffs[*inbuf][1]; + inbuf++; + } + + for (ch = 0; ch < fmt->channels; ch++) + { + delta[ch] = inbuf[0] | (SE(inbuf[1]) << 8); + inbuf += 2; + } + + for (ch = 0; ch < fmt->channels; ch++) + { + sample[ch][0] = inbuf[0] | (SE(inbuf[1]) << 8); + inbuf += 2; + } + + for (ch = 0; ch < fmt->channels; ch++) + { + sample[ch][1] = inbuf[0] | (SE(inbuf[1]) << 8); + inbuf += 2; + } + + inbufsize -= 7 * fmt->channels; + ch = fmt->channels - 1; + + while (size-- > 0) + { + *outbuf++ = create_pcmdata(0, *inbuf >> 4 ) << 13; + *outbuf++ = create_pcmdata(ch, *inbuf & 0xf) << 13; + nsamples += 2; + + inbuf++; + inbufsize--; + if (inbufsize <= 0) + break; + } + + if (fmt->channels == 2) + nsamples >>= 1; + *outbufcount = nsamples; + + return CODEC_OK; +} + +static const struct pcm_codec codec = { + set_format, + get_seek_pos, + decode, + }; + +const struct pcm_codec *get_ms_adpcm_codec(void) +{ + return &codec; +} diff --git a/apps/codecs/libpcm/yamaha_adpcm.c b/apps/codecs/libpcm/yamaha_adpcm.c new file mode 100644 index 0000000..6b3daa8 --- /dev/null +++ b/apps/codecs/libpcm/yamaha_adpcm.c @@ -0,0 +1,245 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 Yoshihisa Uchida + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "codeclib.h" +#include "pcm_common.h" +#include "adpcm_seek.h" + +/* + * YAMAHA ADPCM + * + * References + * [1] YAMAHA, YAMAHA ADPCM ACM Driver Version 1.0.0.0, 2005 + * [2] BlendWorks, YM2608 ADPCM, + * http://web.archive.org/web/20050208190547/www.memb.jp/~dearna/ma/ym2608/adpcm.html + * [3] Naoyuki Sawa, ADPCM no shikumi #1, + * http://www.piece-me.org/piece-lab/adpcm/adpcm1.html + * [4] ffmpeg source code, libavcodec/adpcm.c + */ + +/* ADPCM data block layout + * + * when the block header exists. (for example, encoding by YAMAHA ADPCM ACM Driver) + * blockAlign = (frequency / 60 + 4) * channels. + * + * block + * (channels = 1) + * int16_t first value (Little endian) + * uint16_t first predictor (Little endian) + * uint8_t ADPCM data (1st data: 0-3 bit, 2nd data: 4-7 bit) + * .... + * + * (channels = 2) + * int16_t Left channel first value (Little endian) + * uint16_t Left channel first predictor (Little endian) + * int16_t Right channel first value (Little endian) + * uint16_t Right channel first predictor (Little endian) + * uint8_t ADPCM data (Left channel: 0-3 bit, Right channel: 4-7 bit) + * .... + * + * when the block header does not exist. (for example, encoding by ffmpeg) + * blockAlign = 8000 + * + * block + * (channels = 1) + * uint8_t ADPCM data (1st data: 0-3 bit, 2nd data: 4-7 bit) + * .... + * + * (channels = 2) + * uint8_t ADPCM data (Left channel: 0-3 bit, Right channel: 4-7 bit) + * .... + */ + +static const int32_t amplification_table[] ICONST_ATTR = { + 230, 230, 230, 230, 307, 409, 512, 614, 230, 230, 230, 230, 307, 409, 512, 614 +}; + +static bool has_block_header = false; + +static struct adpcm_data cur_data; +static int blocksperchunk; + +static struct pcm_format *fmt; + +static bool set_format(struct pcm_format *format) +{ + fmt = format; + + if (fmt->bitspersample != 4) + { + DEBUGF("CODEC_ERROR: yamaha adpcm must be 4 bitspersample: %d\n", + fmt->bitspersample); + return false; + } + + /* check exists block header */ + if (fmt->blockalign == ((ci->id3->frequency / 60) + 4) * fmt->channels) + { + has_block_header = true; + + /* chunksize = about 1/30 [sec] data */ + fmt->chunksize = fmt->blockalign; + blocksperchunk = 1; + } + else + { + uint32_t max_chunk_count; + + has_block_header = false; + + /* blockalign = 2 * channels samples */ + fmt->blockalign = fmt->channels; + fmt->samplesperblock = 2; + + /* chunksize = about 1/32[sec] data */ + blocksperchunk = ci->id3->frequency >> 6; + fmt->chunksize = blocksperchunk * fmt->blockalign; + + max_chunk_count = (uint64_t)ci->id3->length * ci->id3->frequency + / (2000LL * fmt->chunksize / fmt->channels); + + /* initialize seek table */ + init_seek_table(max_chunk_count); + /* add first data */ + add_adpcm_data(&cur_data); + } + + return true; +} + +static int16_t create_pcmdata(int ch, uint8_t nibble) +{ + int32_t tmp_pcmdata = cur_data.pcmdata[ch]; + int32_t step = cur_data.step[ch]; + int32_t delta = step >> 3; + + if (nibble & 4) delta += step; + if (nibble & 2) delta += (step >> 1); + if (nibble & 1) delta += (step >> 2); + + if (nibble & 0x08) + tmp_pcmdata -= delta; + else + tmp_pcmdata += delta; + + CLIP(tmp_pcmdata, -32768, 32767); + cur_data.pcmdata[ch] = tmp_pcmdata; + + step = (step * amplification_table[nibble & 0x07]) >> 8; + CLIP(step, 127, 24576); + cur_data.step[ch] = step; + + return cur_data.pcmdata[ch]; +} + +static int decode(const uint8_t *inbuf, size_t inbufsize, + int32_t *outbuf, int *outbufcount) +{ + int ch; + size_t nsamples = 0; + + /* read block header */ + if (has_block_header) + { + for (ch = 0; ch < fmt->channels; ch++) + { + cur_data.pcmdata[ch] = inbuf[0] | (SE(inbuf[1]) << 8); + cur_data.step[ch] = inbuf[2] | (inbuf[3] << 8); + + inbuf += 4; + inbufsize -= 4; + } + } + + /* read block data */ + ch = fmt->channels - 1; + while (inbufsize) + { + *outbuf++ = create_pcmdata(0, *inbuf ) << 13; + *outbuf++ = create_pcmdata(ch, *inbuf >> 4) << 13; + nsamples += 2; + + inbuf++; + inbufsize--; + } + + if (fmt->channels == 2) + nsamples >>= 1; + *outbufcount = nsamples; + + if (!has_block_header) + add_adpcm_data(&cur_data); + + return CODEC_OK; +} + +static int decode_for_seek(const uint8_t *inbuf, size_t inbufsize) +{ + int ch = fmt->channels - 1; + + while (inbufsize) + { + create_pcmdata(0, *inbuf ); + create_pcmdata(ch, *inbuf >> 4); + + inbuf++; + inbufsize--; + } + + add_adpcm_data(&cur_data); + + return CODEC_OK; +} + +static struct pcm_pos *get_seek_pos(long seek_time, + uint8_t *(*read_buffer)(size_t *realsize)) +{ + static struct pcm_pos newpos; + uint32_t new_count= 0; + + if (seek_time > 0) + new_count = ((uint64_t)seek_time * ci->id3->frequency + / (1000LL * fmt->samplesperblock)) / blocksperchunk; + + if (!has_block_header) + { + new_count = seek(new_count, &cur_data, read_buffer, &decode_for_seek); + } + newpos.pos = new_count * fmt->chunksize; + newpos.samples = new_count * blocksperchunk * fmt->samplesperblock; + return &newpos; +} + +static struct pcm_codec codec = { + set_format, + get_seek_pos, + decode, + }; + +const struct pcm_codec *get_yamaha_adpcm_codec(void) +{ + /* initialize first step, pcm data */ + cur_data.pcmdata[0] = 0; + cur_data.pcmdata[1] = 0; + cur_data.step[0] = 127; + cur_data.step[1] = 127; + + return &codec; +}