rockbox/lib/rbcodec/codecs/libpcm/yamaha_adpcm.c
Sean Bartell f40bfc9267 Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
2012-04-25 22:13:20 +02:00

250 lines
7.1 KiB
C

/***************************************************************************
* __________ __ ___.
* 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 "adpcm_seek.h"
#include "support_formats.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
* <Mono> (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)
* ....
*
* <Stereo> (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
* <Mono> (channels = 1)
* uint8_t ADPCM data (1st data: 0-3 bit, 2nd data: 4-7 bit)
* ....
*
* <Stereo> (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->channels == 0)
{
DEBUGF("CODEC_ERROR: channels is 0\n");
return false;
}
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 ) << (PCM_OUTPUT_DEPTH - 16);
*outbuf++ = create_pcmdata(ch, *inbuf >> 4) << (PCM_OUTPUT_DEPTH - 16);
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(uint32_t seek_val, int seek_mode,
uint8_t *(*read_buffer)(size_t *realsize))
{
static struct pcm_pos newpos;
uint32_t new_count = (seek_mode == PCM_SEEK_TIME)?
((uint64_t)seek_val * ci->id3->frequency / 1000LL)
/ (blocksperchunk * fmt->samplesperblock) :
seek_val / (unsigned long)fmt->chunksize;
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;
}