diff --git a/apps/codecs/libpcms/SOURCES b/apps/codecs/libpcms/SOURCES index 89be345..449bdbb 100644 --- a/apps/codecs/libpcms/SOURCES +++ b/apps/codecs/libpcms/SOURCES @@ -2,3 +2,6 @@ linear_pcm.c itut_g711.c dvi_adpcm.c ieee_float.c +dialogic_oki_adpcm.c +ms_adpcm.c +yamaha_adpcm.c diff --git a/apps/codecs/libpcms/pcm_common.h b/apps/codecs/libpcms/pcm_common.h index 757d0ad..a920c93 100644 --- a/apps/codecs/libpcms/pcm_common.h +++ b/apps/codecs/libpcms/pcm_common.h @@ -38,6 +38,11 @@ /* 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; + struct pcm_format { /* * RIFF: wFormatTag (in 'fmt ' chunk) diff --git a/apps/codecs/libpcms/support_formats.h b/apps/codecs/libpcms/support_formats.h index 0a6ea33..b6d3d64 100644 --- a/apps/codecs/libpcms/support_formats.h +++ b/apps/codecs/libpcms/support_formats.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 index 6fece78..9acf00f 100644 --- 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) { diff --git a/apps/metadata/wave.c b/apps/metadata/wave.c old mode 100644 new mode 100755 index cf676f8..8ca68c1 --- a/apps/metadata/wave.c +++ b/apps/metadata/wave.c @@ -29,17 +29,79 @@ #include "metadata_common.h" #include "metadata_parsers.h" +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 */ +}; + +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; 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)) @@ -66,25 +128,31 @@ 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 || (read_bytes = read(fd, buf, i)) < i) return false; - i -= 16; - - /* skipping wFormatTag */ + /* 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; - /* skipping wBlockAlign */ + /* wBlockAlign */ + fmt.blockalign = buf[12] | (buf[13] << 8); /* wBitsPerSample */ - bitspersample = buf[14] | (buf[15] << 8); + fmt.bitspersample = buf[14] | (buf[15] << 8); + if (i > 19) + { + /* wSamplesPerBlock */ + fmt.samplesperblock = buf[18] | (buf[19] << 8); + } + i = 0; } else if (memcmp(buf, "data", 4) == 0) { - numbytes = i; + fmt.numbytes = i; break; } else if (memcmp(buf, "fact", 4) == 0) @@ -109,16 +177,14 @@ bool get_wave_metadata(int fd, struct mp3entry* id3) return false; } - if ((numbytes == 0) || (channels == 0)) + if ((fmt.numbytes == 0) || (fmt.channels == 0) || (fmt.blockalign == 0)) { 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/libpcms/dialogic_oki_adpcm.c b/apps/codecs/libpcms/dialogic_oki_adpcm.c new file mode 100755 index 0000000..eebce96 --- /dev/null +++ b/apps/codecs/libpcms/dialogic_oki_adpcm.c @@ -0,0 +1,138 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 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" + +/* + * 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 int8_t index; +static int16_t pcmdata; + +static struct pcm_format *fmt; + +static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) +{ + fmt = format; + + (void)fmtpos; + + 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 = 8; + + /* chunksize = about 1/50[sec] data */ + fmt->chunksize = (ci->id3->frequency / 100); + + return true; +} + +static uint32_t get_seek_pos(long seek_time) +{ + return ((uint64_t)(seek_time * ci->id3->frequency)) / 250LL; +} + +static int16_t create_pcmdata(uint8_t nibble) +{ + int16_t delta; + 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) + pcmdata -= delta; + else + pcmdata += delta; + + CLIP(pcmdata, -2048, 2047); + + index += index_table[nibble & 0x07]; + CLIP(index, 0, 48); + + return pcmdata; +} + +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) << 16; + *outbuf++ = create_pcmdata(*inbuf ) << 16; + nsamples += 2; + + inbuf++; + inbufsize--; + } + + *outbufcount = nsamples; + + return CODEC_OK; +} + +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 */ + pcmdata = 0; + index = 0; + + return &codec; +} diff --git a/apps/codecs/libpcms/ms_adpcm.c b/apps/codecs/libpcms/ms_adpcm.c new file mode 100755 index 0000000..addcdf3 --- /dev/null +++ b/apps/codecs/libpcms/ms_adpcm.c @@ -0,0 +1,193 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 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 NUM_COEFF 7 +static int16_t coeffs[NUM_COEFF][2]; + +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, const unsigned char *fmtpos) +{ + int i; + int num; + int size; + + 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; + + fmtpos += 4; /* skip 'fmt ' */ + size = fmtpos[0] | (fmtpos[1] << 8) | (fmtpos[1] << 16) | (fmtpos[1] << 24); + if (size < 50) + { + DEBUGF("CODEC_ERROR: microsoft adpcm 'fmt ' chunk size=%lu < 50\n", (unsigned long)size); + return false; + } + + /* get nNumCoef */ + fmtpos += 24; + num = fmtpos[0] | (fmtpos[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 != NUM_COEFF) + { + DEBUGF("CODEC_ERROR: microsoft adpcm nNumCoef=%d != 7\n", num); + return false; + } + + /* get aCoeffs */ + fmtpos += 2; + for (i = 0; i < NUM_COEFF; i++) + { + coeffs[i][0] = fmtpos[0] | (SE(fmtpos[1]) << 8); + coeffs[i][1] = fmtpos[2] | (SE(fmtpos[3]) << 8); + fmtpos += 4; + } + + return true; +} + +static uint32_t get_seek_pos(long seek_time) +{ + return ((uint64_t)(seek_time * ci->id3->frequency * fmt->blockalign)) / + (2000LL * fmt->samplesperblock); +} + +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 >= NUM_COEFF) + { + DEBUGF("CODEC_ERROR: microsoft adpcm illegal initial coeff=%d > 7\n", *inbuf); + return CODEC_ERROR; + } + dec_coeff[ch][0] = coeffs[*inbuf][0]; + dec_coeff[ch][1] = 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/libpcms/yamaha_adpcm.c b/apps/codecs/libpcms/yamaha_adpcm.c new file mode 100755 index 0000000..ed539e3 --- /dev/null +++ b/apps/codecs/libpcms/yamaha_adpcm.c @@ -0,0 +1,204 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 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" + +/* + * 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 + * + * 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 data: 0-3 bit, Right channel data: 4-7 bit) + * .... + * + * 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 data: 0-3 bit, Right channel data: 4-7 bit) + * .... + */ + +static const int amplification_table[] ICONST_ATTR = { + 230, 230, 230, 230, 307, 409, 512, 614, 230, 230, 230, 230, 307, 409, 512, 614 +}; + +static uint32_t step[2] = {127, 127}; +static int32_t pcmdata[2] = {0, 0}; + +static bool has_block_header = false; + +static struct pcm_format *fmt; + +static bool set_format(struct pcm_format *format, const unsigned char *fmtpos) +{ + fmt = format; + + (void)fmtpos; + + 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; + } + else + { + has_block_header = false; + + /* blockalign = 2 * channels samples */ + fmt->blockalign = 8 * fmt->channels; + + /* chunksize = about 1/50[sec] data */ + fmt->chunksize = (ci->id3->frequency / 100) * fmt->channels; + } + + return true; +} + +static uint32_t get_seek_pos(long seek_time) +{ + uint32_t newpos = 0; + + if (has_block_header) + { + /* blockalign = 1/30 [sec] data = (frequency / 30) samples */ + newpos = (seek_time * 3/100) * fmt->blockalign; + } + else + { + /* blockalign = 2 * channels samples */ + newpos = ((uint64_t)(seek_time * ci->id3->frequency * fmt->channels)) / 250LL; + } + + return newpos; +} + +static int16_t create_pcmdata(int ch, uint8_t nibble) +{ + int32_t delta = 0; + + delta = step[ch] >> 3; + if (nibble & 4) delta += step[ch]; + if (nibble & 2) delta += (step[ch] >> 1); + if (nibble & 1) delta += (step[ch] >> 2); + + if (nibble & 0x08) + pcmdata[ch] -= delta; + else + pcmdata[ch] += delta; + + CLIP(pcmdata[ch], -32768, 32767); + + step[ch] = (step[ch] * amplification_table[nibble & 0x07]) >> 8; + CLIP(step[ch], 127, 24576); + + return (int16_t)pcmdata[ch]; +} + +static int decode(const uint8_t *inbuf, size_t inbufsize, + int32_t *outbuf, int *outbufcount) +{ + int ch; + size_t nsamples = 0; + + /* parse block header */ + if (has_block_header) + { + for (ch = 0; ch < fmt->channels; ch++) + { + pcmdata[ch] = inbuf[0] | (SE(inbuf[1]) << 8); + step[ch] = inbuf[2] | (inbuf[3] << 8); + + inbuf += 4; + inbufsize -= 4; + } + } + + 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; + + return CODEC_OK; +} + +static const struct pcm_codec codec = { + set_format, + get_seek_pos, + decode, + }; + +const struct pcm_codec *get_yamaha_adpcm_codec(void) +{ + /* initialize first step, pcm data */ + step[0] = 127; + step[1] = 127; + pcmdata[0] = 0; + pcmdata[1] = 0; + + return &codec; +}