diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
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>
Diffstat (limited to 'lib/rbcodec/codecs')
741 files changed, 247480 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/SOURCES b/lib/rbcodec/codecs/SOURCES new file mode 100644 index 0000000000..db6e82c75f --- /dev/null +++ b/lib/rbcodec/codecs/SOURCES @@ -0,0 +1,54 @@ +/* decoders */ + +vorbis.c +mpa.c +flac.c +wav.c +a52.c +wavpack.c +#ifndef RB_PROFILE +alac.c +#endif +cook.c +raac.c +a52_rm.c +atrac3_rm.c +atrac3_oma.c +mpc.c +wma.c +sid.c +ape.c +asap.c +aac.c +spc.c +mod.c +shorten.c +aiff.c +speex.c +adx.c +smaf.c +au.c +vox.c +wav64.c +tta.c +wmapro.c +ay.c +gbs.c +hes.c +nsf.c +sgc.c +vgm.c +#if MEMORYSIZE > 2 +kss.c +#endif + +#ifdef HAVE_RECORDING + +/* encoders */ + +aiff_enc.c +mp3_enc.c +wav_enc.c +wavpack_enc.c + +#endif /* HAVE_RECORDING */ diff --git a/lib/rbcodec/codecs/a52.c b/lib/rbcodec/codecs/a52.c new file mode 100644 index 0000000000..cb6e66dd05 --- /dev/null +++ b/lib/rbcodec/codecs/a52.c @@ -0,0 +1,192 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 Dave Chapman + * + * 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 <inttypes.h> /* Needed by a52.h */ +#include <codecs/liba52/config-a52.h> +#include <codecs/liba52/a52.h> + +CODEC_HEADER + +#define BUFFER_SIZE 4096 + +#define A52_SAMPLESPERFRAME (6*256) + +static a52_state_t *state; +static unsigned long samplesdone; +static unsigned long frequency; + +/* used outside liba52 */ +static uint8_t buf[3840] IBSS_ATTR; + +static inline void output_audio(sample_t *samples) +{ + ci->yield(); + ci->pcmbuf_insert(&samples[0], &samples[256], 256); +} + +static void a52_decode_data(uint8_t *start, uint8_t *end) +{ + static uint8_t *bufptr = buf; + static uint8_t *bufpos = buf + 7; + /* + * sample_rate and flags are static because this routine could + * exit between the a52_syncinfo() and the ao_setup(), and we want + * to have the same values when we get back ! + */ + static int sample_rate; + static int flags; + int bit_rate; + int len; + + while (1) { + len = end - start; + if (!len) + break; + if (len > bufpos - bufptr) + len = bufpos - bufptr; + memcpy(bufptr, start, len); + bufptr += len; + start += len; + if (bufptr == bufpos) { + if (bufpos == buf + 7) { + int length; + + length = a52_syncinfo(buf, &flags, &sample_rate, &bit_rate); + if (!length) { + //DEBUGF("skip\n"); + for (bufptr = buf; bufptr < buf + 6; bufptr++) + bufptr[0] = bufptr[1]; + continue; + } + bufpos = buf + length; + } else { + /* Unity gain is 1 << 26, and we want to end up on 28 bits + of precision instead of the default 30. + */ + level_t level = 1 << 24; + sample_t bias = 0; + int i; + + /* This is the configuration for the downmixing: */ + flags = A52_STEREO | A52_ADJUST_LEVEL; + + if (a52_frame(state, buf, &flags, &level, bias)) + goto error; + a52_dynrng(state, NULL, NULL); + frequency = sample_rate; + + /* An A52 frame consists of 6 blocks of 256 samples + So we decode and output them one block at a time */ + for (i = 0; i < 6; i++) { + if (a52_block(state)) + goto error; + output_audio(a52_samples(state)); + samplesdone += 256; + } + ci->set_elapsed(samplesdone/(frequency/1000)); + bufptr = buf; + bufpos = buf + 7; + continue; + error: + //logf("Error decoding A52 stream\n"); + bufptr = buf; + bufpos = buf + 7; + } + } + } +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); + ci->configure(DSP_SET_SAMPLE_DEPTH, 28); + } + else if (reason == CODEC_UNLOAD) { + if (state) + a52_free(state); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + size_t n; + unsigned char *filebuf; + int sample_loc; + intptr_t param; + + if (codec_init()) + return CODEC_ERROR; + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + codec_set_replaygain(ci->id3); + + /* Intialise the A52 decoder and check for success */ + state = a52_init(0); + + samplesdone = 0; + + /* The main decoding loop */ + if (ci->id3->offset) { + if (ci->seek_buffer(ci->id3->offset)) { + samplesdone = (ci->id3->offset / ci->id3->bytesperframe) * + A52_SAMPLESPERFRAME; + ci->set_elapsed(samplesdone/(ci->id3->frequency / 1000)); + } + } + else { + ci->seek_buffer(ci->id3->first_frame_offset); + ci->set_elapsed(0); + } + + while (1) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + sample_loc = param/1000 * ci->id3->frequency; + + if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) { + samplesdone = sample_loc; + ci->set_elapsed(samplesdone/(ci->id3->frequency/1000)); + } + ci->seek_complete(); + } + + filebuf = ci->request_buffer(&n, BUFFER_SIZE); + + if (n == 0) /* End of Stream */ + break; + + a52_decode_data(filebuf, filebuf + n); + ci->advance_buffer(n); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/a52_rm.c b/lib/rbcodec/codecs/a52_rm.c new file mode 100644 index 0000000000..2db126f5b1 --- /dev/null +++ b/lib/rbcodec/codecs/a52_rm.c @@ -0,0 +1,227 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Mohamed Tarek + * + * 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 <codecs/librm/rm.h> +#include <inttypes.h> /* Needed by a52.h */ +#include <codecs/liba52/config-a52.h> +#include <codecs/liba52/a52.h> + +CODEC_HEADER + +#define BUFFER_SIZE 4096 + +#define A52_SAMPLESPERFRAME (6*256) + +static a52_state_t *state; +static unsigned long samplesdone; +static unsigned long frequency; +static RMContext rmctx; +static RMPacket pkt; + +static void init_rm(RMContext *rmctx) +{ + memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); +} + +/* used outside liba52 */ +static uint8_t buf[3840] IBSS_ATTR; + +/* The following two functions, a52_decode_data and output_audio are taken from a52.c */ +static inline void output_audio(sample_t *samples) +{ + ci->yield(); + ci->pcmbuf_insert(&samples[0], &samples[256], 256); +} + +static void a52_decode_data(uint8_t *start, uint8_t *end) +{ + static uint8_t *bufptr = buf; + static uint8_t *bufpos = buf + 7; + /* + * sample_rate and flags are static because this routine could + * exit between the a52_syncinfo() and the ao_setup(), and we want + * to have the same values when we get back ! + */ + static int sample_rate; + static int flags; + int bit_rate; + int len; + + while (1) { + len = end - start; + if (!len) + break; + if (len > bufpos - bufptr) + len = bufpos - bufptr; + memcpy(bufptr, start, len); + bufptr += len; + start += len; + if (bufptr == bufpos) { + if (bufpos == buf + 7) { + int length; + + length = a52_syncinfo(buf, &flags, &sample_rate, &bit_rate); + if (!length) { + //DEBUGF("skip\n"); + for (bufptr = buf; bufptr < buf + 6; bufptr++) + bufptr[0] = bufptr[1]; + continue; + } + bufpos = buf + length; + } else { + /* Unity gain is 1 << 26, and we want to end up on 28 bits + of precision instead of the default 30. + */ + level_t level = 1 << 24; + sample_t bias = 0; + int i; + + /* This is the configuration for the downmixing: */ + flags = A52_STEREO | A52_ADJUST_LEVEL; + + if (a52_frame(state, buf, &flags, &level, bias)) + goto error; + a52_dynrng(state, NULL, NULL); + frequency = sample_rate; + + /* An A52 frame consists of 6 blocks of 256 samples + So we decode and output them one block at a time */ + for (i = 0; i < 6; i++) { + if (a52_block(state)) + goto error; + output_audio(a52_samples(state)); + samplesdone += 256; + } + ci->set_elapsed(samplesdone/(frequency/1000)); + bufptr = buf; + bufpos = buf + 7; + continue; + error: + //logf("Error decoding A52 stream\n"); + bufptr = buf; + bufpos = buf + 7; + } + } + } +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); + ci->configure(DSP_SET_SAMPLE_DEPTH, 28); + } + else if (reason == CODEC_UNLOAD) { + if (state) + a52_free(state); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + size_t n; + uint8_t *filebuf; + int consumed, packet_offset; + int playback_on = -1; + size_t resume_offset; + intptr_t param; + enum codec_command_action action = CODEC_ACTION_NULL; + + if (codec_init()) { + return CODEC_ERROR; + } + + resume_offset = ci->id3->offset; + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + codec_set_replaygain(ci->id3); + + ci->seek_buffer(ci->id3->first_frame_offset); + + /* Intializations */ + state = a52_init(0); + ci->memset(&rmctx,0,sizeof(RMContext)); + ci->memset(&pkt,0,sizeof(RMPacket)); + init_rm(&rmctx); + + samplesdone = 0; + + /* check for a mid-track resume and force a seek time accordingly */ + if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { + resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; + /* put number of subpackets to skip in resume_offset */ + resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); + param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); + action = CODEC_ACTION_SEEK_TIME; + } + else { + /* Seek to the first packet */ + ci->set_elapsed(0); + ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); + } + + /* The main decoding loop */ + while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) { + if (action == CODEC_ACTION_NULL) + action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + packet_offset = param / ((rmctx.block_align*8*1000)/rmctx.bit_rate); + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + + packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE)); + rmctx.audio_pkt_cnt = packet_offset; + samplesdone = (rmctx.sample_rate/1000 * param); + ci->set_elapsed(samplesdone/(frequency/1000)); + ci->seek_complete(); + } + + action = CODEC_ACTION_NULL; + + filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE); + consumed = rm_get_packet(&filebuf, &rmctx, &pkt); + + if(consumed < 0 && playback_on != 0) { + if(playback_on == -1) { + /* Error only if packet-parsing failed and playback hadn't started */ + DEBUGF("rm_get_packet failed\n"); + return CODEC_ERROR; + } + else { + break; + } + } + + playback_on = 1; + a52_decode_data(filebuf, filebuf + rmctx.block_align); + ci->advance_buffer(pkt.length); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/aac.c b/lib/rbcodec/codecs/aac.c new file mode 100644 index 0000000000..365dca804d --- /dev/null +++ b/lib/rbcodec/codecs/aac.c @@ -0,0 +1,297 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 Dave Chapman + * + * 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 "libm4a/m4a.h" +#include "libfaad/common.h" +#include "libfaad/structs.h" +#include "libfaad/decoder.h" + +CODEC_HEADER + +/* The maximum buffer size handled by faad. 12 bytes are required by libfaad + * as headroom (see libfaad/bits.c). FAAD_BYTE_BUFFER_SIZE bytes are buffered + * for each frame. */ +#define FAAD_BYTE_BUFFER_SIZE (2048-12) + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); + ci->configure(DSP_SET_SAMPLE_DEPTH, 29); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + /* Note that when dealing with QuickTime/MPEG4 files, terminology is + * a bit confusing. Files with sound are split up in chunks, where + * each chunk contains one or more samples. Each sample in turn + * contains a number of "sound samples" (the kind you refer to with + * the sampling frequency). + */ + size_t n; + demux_res_t demux_res; + stream_t input_stream; + uint32_t sound_samples_done; + uint32_t elapsed_time; + int file_offset; + int framelength; + int lead_trim = 0; + unsigned int frame_samples; + unsigned int i; + unsigned char* buffer; + NeAACDecFrameInfo frame_info; + NeAACDecHandle decoder; + int err; + uint32_t seek_idx = 0; + uint32_t s = 0; + uint32_t sbr_fac = 1; + unsigned char c = 0; + void *ret; + intptr_t param; + bool empty_first_frame = false; + + /* Clean and initialize decoder structures */ + memset(&demux_res , 0, sizeof(demux_res)); + if (codec_init()) { + LOGF("FAAD: Codec init error\n"); + return CODEC_ERROR; + } + + file_offset = ci->id3->offset; + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + codec_set_replaygain(ci->id3); + + stream_create(&input_stream,ci); + + ci->seek_buffer(ci->id3->first_frame_offset); + + /* if qtmovie_read returns successfully, the stream is up to + * the movie data, which can be used directly by the decoder */ + if (!qtmovie_read(&input_stream, &demux_res)) { + LOGF("FAAD: File init error\n"); + return CODEC_ERROR; + } + + /* initialise the sound converter */ + decoder = NeAACDecOpen(); + + if (!decoder) { + LOGF("FAAD: Decode open error\n"); + return CODEC_ERROR; + } + + NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); + conf->outputFormat = FAAD_FMT_24BIT; /* irrelevant, we don't convert */ + NeAACDecSetConfiguration(decoder, conf); + + err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c); + if (err) { + LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); + return CODEC_ERROR; + } + +#ifdef SBR_DEC + /* Check for need of special handling for seek/resume and elapsed time. */ + if (ci->id3->needs_upsampling_correction) { + sbr_fac = 2; + } else { + sbr_fac = 1; + } +#endif + + i = 0; + + if (file_offset > 0) { + /* Resume the desired (byte) position. Important: When resuming SBR + * upsampling files the resulting sound_samples_done must be expanded + * by a factor of 2. This is done via using sbr_fac. */ + if (m4a_seek_raw(&demux_res, &input_stream, file_offset, + &sound_samples_done, (int*) &i)) { + sound_samples_done *= sbr_fac; + } else { + sound_samples_done = 0; + } + NeAACDecPostSeekReset(decoder, i); + } else { + sound_samples_done = 0; + } + + elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsed_time); + + if (i == 0) + { + lead_trim = ci->id3->lead_trim; + } + + /* The main decoding loop */ + while (i < demux_res.num_sample_byte_sizes) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + /* Deal with any pending seek requests */ + if (action == CODEC_ACTION_SEEK_TIME) { + /* Seek to the desired time position. Important: When seeking in SBR + * upsampling files the seek_time must be divided by 2 when calling + * m4a_seek and the resulting sound_samples_done must be expanded + * by a factor 2. This is done via using sbr_fac. */ + if (m4a_seek(&demux_res, &input_stream, + (param/10/sbr_fac)*(ci->id3->frequency/100), + &sound_samples_done, (int*) &i)) { + sound_samples_done *= sbr_fac; + elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsed_time); + seek_idx = 0; + + if (i == 0) + { + lead_trim = ci->id3->lead_trim; + } + } + NeAACDecPostSeekReset(decoder, i); + ci->seek_complete(); + } + + /* There can be gaps between chunks, so skip ahead if needed. It + * doesn't seem to happen much, but it probably means that a + * "proper" file can have chunks out of order. Why one would want + * that an good question (but files with gaps do exist, so who + * knows?), so we don't support that - for now, at least. + */ + file_offset = m4a_check_sample_offset(&demux_res, i, &seek_idx); + + if (file_offset > ci->curpos) + { + ci->advance_buffer(file_offset - ci->curpos); + } + else if (file_offset == 0) + { + LOGF("AAC: get_sample_offset error\n"); + return CODEC_ERROR; + } + + /* Request the required number of bytes from the input buffer */ + buffer=ci->request_buffer(&n, FAAD_BYTE_BUFFER_SIZE); + + /* Decode one block - returned samples will be host-endian */ + ret = NeAACDecDecode(decoder, &frame_info, buffer, n); + + /* NeAACDecDecode may sometimes return NULL without setting error. */ + if (ret == NULL || frame_info.error > 0) { + LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); + return CODEC_ERROR; + } + + /* Advance codec buffer (no need to call set_offset because of this) */ + ci->advance_buffer(frame_info.bytesconsumed); + + /* Output the audio */ + ci->yield(); + + frame_samples = frame_info.samples >> 1; + + if (empty_first_frame) + { + /* Remove the first frame from lead_trim, under the assumption + * that it had the same size as this frame + */ + empty_first_frame = false; + lead_trim -= frame_samples; + + if (lead_trim < 0) + { + lead_trim = 0; + } + } + + /* Gather number of samples for the decoded frame. */ + framelength = frame_samples - lead_trim; + + if (i == demux_res.num_sample_byte_sizes - 1) + { + // Size of the last frame + const uint32_t sample_duration = (demux_res.num_time_to_samples > 0) ? + demux_res.time_to_sample[demux_res.num_time_to_samples - 1].sample_duration : + frame_samples; + + /* Currently limited to at most one frame of tail_trim. + * Seems to be enough. + */ + if (ci->id3->tail_trim == 0 && sample_duration < frame_samples) + { + /* Subtract lead_trim just in case we decode a file with only + * one audio frame with actual data (lead_trim is usually zero + * here). + */ + framelength = sample_duration - lead_trim; + } + else + { + framelength -= ci->id3->tail_trim; + } + } + + if (framelength > 0) + { + ci->pcmbuf_insert(&decoder->time_out[0][lead_trim], + &decoder->time_out[1][lead_trim], + framelength); + sound_samples_done += framelength; + /* Update the elapsed-time indicator */ + elapsed_time = ((uint64_t) sound_samples_done * 1000) / + ci->id3->frequency; + ci->set_elapsed(elapsed_time); + } + + if (lead_trim > 0) + { + /* frame_info.samples can be 0 for frame 0. We still want to + * remove it from lead_trim, so do that during frame 1. + */ + if (0 == i && 0 == frame_info.samples) + { + empty_first_frame = true; + } + + lead_trim -= frame_samples; + + if (lead_trim < 0) + { + lead_trim = 0; + } + } + + ++i; + } + + LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done); + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/adx.c b/lib/rbcodec/codecs/adx.c new file mode 100644 index 0000000000..0c67fc8d6e --- /dev/null +++ b/lib/rbcodec/codecs/adx.c @@ -0,0 +1,404 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2006-2008 Adam Gashlin (hcs) + * Copyright (C) 2006 Jens Arnold + * + * 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 <limits.h> +#include "codeclib.h" +#include "inttypes.h" +#include "math.h" +#include "lib/fixedpoint.h" + +CODEC_HEADER + +/* Maximum number of bytes to process in one iteration */ +#define WAV_CHUNK_SIZE (1024*2) + +/* Number of times to loop looped tracks when repeat is disabled */ +#define LOOP_TIMES 2 + +/* Length of fade-out for looped tracks (milliseconds) */ +#define FADE_LENGTH 10000L + +/* Default high pass filter cutoff frequency is 500 Hz. + * Others can be set, but the default is nearly always used, + * and there is no way to determine if another was used, anyway. + */ +static const long cutoff = 500; + +static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR; + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + /* we only render 16 bits */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + int channels; + int sampleswritten, i; + uint8_t *buf; + int32_t ch1_1, ch1_2, ch2_1, ch2_2; /* ADPCM history */ + size_t n; + int endofstream; /* end of stream flag */ + uint32_t avgbytespersec; + int looping; /* looping flag */ + int loop_count; /* number of loops done so far */ + int fade_count; /* countdown for fadeout */ + int fade_frames; /* length of fade in frames */ + off_t start_adr, end_adr; /* loop points */ + off_t chanstart, bufoff; + /*long coef1=0x7298L,coef2=-0x3350L;*/ + long coef1, coef2; + intptr_t param; + + DEBUGF("ADX: next_track\n"); + if (codec_init()) { + return CODEC_ERROR; + } + DEBUGF("ADX: after init\n"); + + /* init history */ + ch1_1=ch1_2=ch2_1=ch2_2=0; + + codec_set_replaygain(ci->id3); + + /* Get header */ + DEBUGF("ADX: request initial buffer\n"); + ci->seek_buffer(0); + buf = ci->request_buffer(&n, 0x38); + if (!buf || n < 0x38) { + return CODEC_ERROR; + } + bufoff = 0; + DEBUGF("ADX: read size = %lx\n",(unsigned long)n); + + /* Get file header for starting offset, channel count */ + + chanstart = ((buf[2] << 8) | buf[3]) + 4; + channels = buf[7]; + + /* useful for seeking and reporting current playback position */ + avgbytespersec = ci->id3->frequency * 18 * channels / 32; + DEBUGF("avgbytespersec=%ld\n",(unsigned long)avgbytespersec); + + /* calculate filter coefficients */ + + /** + * A simple table of these coefficients would be nice, but + * some very odd frequencies are used and if I'm going to + * interpolate I might as well just go all the way and + * calclate them precisely. + * Speed is not an issue as this only needs to be done once per file. + */ + { + const int64_t big28 = 0x10000000LL; + const int64_t big32 = 0x100000000LL; + int64_t frequency = ci->id3->frequency; + int64_t phasemultiple = cutoff*big32/frequency; + + long z; + int64_t a; + const int64_t b = (M_SQRT2*big28)-big28; + int64_t c; + int64_t d; + + fp_sincos((unsigned long)phasemultiple,&z); + + a = (M_SQRT2*big28) - (z >> 3); + + /** + * In the long passed to fsqrt there are only 4 nonfractional bits, + * which is sufficient here, but this is the only reason why I don't + * use 32 fractional bits everywhere. + */ + d = fp_sqrt((a+b)*(a-b)/big28,28); + c = (a-d)*big28/b; + + coef1 = (c*8192) >> 28; + coef2 = (c*c/big28*-4096) >> 28; + DEBUGF("ADX: samprate=%ld ",(long)frequency); + DEBUGF("coef1 %04x ",(unsigned int)(coef1*4)); + DEBUGF("coef2 %04x\n",(unsigned int)(coef2*-4)); + } + + /* Get loop data */ + + looping = 0; start_adr = 0; end_adr = 0; + if (!memcmp(buf+0x10,"\x01\xF4\x03",3)) { + /* Soul Calibur 2 style (type 03) */ + DEBUGF("ADX: type 03 found\n"); + /* check if header is too small for loop data */ + if (chanstart-6 < 0x2c) looping=0; + else { + looping = (buf[0x18]) || + (buf[0x19]) || + (buf[0x1a]) || + (buf[0x1b]); + end_adr = (buf[0x28]<<24) | + (buf[0x29]<<16) | + (buf[0x2a]<<8) | + (buf[0x2b]); + + start_adr = ( + (buf[0x1c]<<24) | + (buf[0x1d]<<16) | + (buf[0x1e]<<8) | + (buf[0x1f]) + )/32*channels*18+chanstart; + } + } else if (!memcmp(buf+0x10,"\x01\xF4\x04",3)) { + /* Standard (type 04) */ + DEBUGF("ADX: type 04 found\n"); + /* check if header is too small for loop data */ + if (chanstart-6 < 0x38) looping=0; + else { + looping = (buf[0x24]) || + (buf[0x25]) || + (buf[0x26]) || + (buf[0x27]); + end_adr = (buf[0x34]<<24) | + (buf[0x35]<<16) | + (buf[0x36]<<8) | + buf[0x37]; + start_adr = ( + (buf[0x28]<<24) | + (buf[0x29]<<16) | + (buf[0x2a]<<8) | + (buf[0x2b]) + )/32*channels*18+chanstart; + } + } else { + DEBUGF("ADX: error, couldn't determine ADX type\n"); + return CODEC_ERROR; + } + + /* is file using encryption */ + if (buf[0x13]==0x08) { + DEBUGF("ADX: error, encrypted ADX not supported\n"); + return false; + } + + if (looping) { + DEBUGF("ADX: looped, start: %lx end: %lx\n",start_adr,end_adr); + } else { + DEBUGF("ADX: not looped\n"); + } + + /* advance to first frame */ + DEBUGF("ADX: first frame at %lx\n",chanstart); + bufoff = chanstart; + + /* get in position */ + ci->seek_buffer(bufoff); + ci->set_elapsed(0); + + /* setup pcm buffer format */ + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + if (channels == 2) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + } else if (channels == 1) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + } else { + DEBUGF("ADX CODEC_ERROR: more than 2 channels\n"); + return CODEC_ERROR; + } + + endofstream = 0; + loop_count = 0; + fade_count = -1; /* disable fade */ + fade_frames = 1; + + /* The main decoder loop */ + + while (!endofstream) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + /* do we need to loop? */ + if (bufoff > end_adr-18*channels && looping) { + DEBUGF("ADX: loop!\n"); + /* check for endless looping */ + if (ci->loop_track()) { + loop_count=0; + fade_count = -1; /* disable fade */ + } else { + /* otherwise start fade after LOOP_TIMES loops */ + loop_count++; + if (loop_count >= LOOP_TIMES && fade_count < 0) { + /* frames to fade over */ + fade_frames = FADE_LENGTH*ci->id3->frequency/32/1000; + /* volume relative to fade_frames */ + fade_count = fade_frames; + DEBUGF("ADX: fade_frames = %d\n",fade_frames); + } + } + bufoff = start_adr; + ci->seek_buffer(bufoff); + } + + /* do we need to seek? */ + if (action == CODEC_ACTION_SEEK_TIME) { + uint32_t newpos; + + DEBUGF("ADX: seek to %ldms\n", (long)param); + + endofstream = 0; + loop_count = 0; + fade_count = -1; /* disable fade */ + fade_frames = 1; + + newpos = (((uint64_t)avgbytespersec*param) + / (1000LL*18*channels))*(18*channels); + bufoff = chanstart + newpos; + while (bufoff > end_adr-18*channels) { + bufoff-=end_adr-start_adr; + loop_count++; + } + ci->seek_buffer(bufoff); + + ci->set_elapsed( + ((end_adr-start_adr)*loop_count + bufoff-chanstart)* + 1000LL/avgbytespersec); + + ci->seek_complete(); + } + + if (bufoff>ci->filesize-channels*18) break; /* End of stream */ + + sampleswritten=0; + + while ( + /* Is there data left in the file? */ + (bufoff <= ci->filesize-(18*channels)) && + /* Is there space in the output buffer? */ + (sampleswritten <= WAV_CHUNK_SIZE-(32*channels)) && + /* Should we be looping? */ + ((!looping) || bufoff <= end_adr-18*channels)) + { + /* decode first/only channel */ + int32_t scale; + int32_t ch1_0, d; + + /* fetch a frame */ + buf = ci->request_buffer(&n, 18); + + if (!buf || n!=18) { + DEBUGF("ADX: couldn't get buffer at %lx\n", + bufoff); + return CODEC_ERROR; + } + + scale = ((buf[0] << 8) | (buf[1])) +1; + + for (i = 2; i < 18; i++) + { + d = (buf[i] >> 4) & 15; + if (d & 8) d-= 16; + ch1_0 = d*scale + ((coef1*ch1_1 + coef2*ch1_2) >> 12); + if (ch1_0 > 32767) ch1_0 = 32767; + else if (ch1_0 < -32768) ch1_0 = -32768; + samples[sampleswritten] = ch1_0; + sampleswritten+=channels; + ch1_2 = ch1_1; ch1_1 = ch1_0; + + d = buf[i] & 15; + if (d & 8) d -= 16; + ch1_0 = d*scale + ((coef1*ch1_1 + coef2*ch1_2) >> 12); + if (ch1_0 > 32767) ch1_0 = 32767; + else if (ch1_0 < -32768) ch1_0 = -32768; + samples[sampleswritten] = ch1_0; + sampleswritten+=channels; + ch1_2 = ch1_1; ch1_1 = ch1_0; + } + bufoff+=18; + ci->advance_buffer(18); + + if (channels == 2) { + /* decode second channel */ + int32_t scale; + int32_t ch2_0, d; + + buf = ci->request_buffer(&n, 18); + + if (!buf || n!=18) { + DEBUGF("ADX: couldn't get buffer at %lx\n", + bufoff); + return CODEC_ERROR; + } + + scale = ((buf[0] << 8)|(buf[1]))+1; + + sampleswritten-=63; + + for (i = 2; i < 18; i++) + { + d = (buf[i] >> 4) & 15; + if (d & 8) d-= 16; + ch2_0 = d*scale + ((coef1*ch2_1 + coef2*ch2_2) >> 12); + if (ch2_0 > 32767) ch2_0 = 32767; + else if (ch2_0 < -32768) ch2_0 = -32768; + samples[sampleswritten] = ch2_0; + sampleswritten+=2; + ch2_2 = ch2_1; ch2_1 = ch2_0; + + d = buf[i] & 15; + if (d & 8) d -= 16; + ch2_0 = d*scale + ((coef1*ch2_1 + coef2*ch2_2) >> 12); + if (ch2_0 > 32767) ch2_0 = 32767; + else if (ch2_0 < -32768) ch2_0 = -32768; + samples[sampleswritten] = ch2_0; + sampleswritten+=2; + ch2_2 = ch2_1; ch2_1 = ch2_0; + } + bufoff+=18; + ci->advance_buffer(18); + sampleswritten--; /* go back to first channel's next sample */ + } + + if (fade_count>0) { + fade_count--; + for (i=0;i<(channels==1?32:64);i++) samples[sampleswritten-i-1]= + ((int32_t)samples[sampleswritten-i-1])*fade_count/fade_frames; + if (fade_count==0) {endofstream=1; break;} + } + } + + if (channels == 2) + sampleswritten >>= 1; /* make samples/channel */ + + ci->pcmbuf_insert(samples, NULL, sampleswritten); + + ci->set_elapsed( + ((end_adr-start_adr)*loop_count + bufoff-chanstart)* + 1000LL/avgbytespersec); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/aiff.c b/lib/rbcodec/codecs/aiff.c new file mode 100644 index 0000000000..333bcd0455 --- /dev/null +++ b/lib/rbcodec/codecs/aiff.c @@ -0,0 +1,350 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (c) 2005 Jvo Studer + * 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 "codecs/libpcm/support_formats.h" + +CODEC_HEADER + +#define FOURCC(c1, c2, c3, c4) \ +((((uint32_t)c1)<<24)|(((uint32_t)c2)<<16)|(((uint32_t)c3)<<8)|((uint32_t)c4)) + +/* This codec supports the following AIFC compressionType formats */ +enum { + AIFC_FORMAT_PCM = FOURCC('N', 'O', 'N', 'E'), /* AIFC PCM Format (big endian) */ + AIFC_FORMAT_ALAW = FOURCC('a', 'l', 'a', 'w'), /* AIFC ALaw compressed */ + AIFC_FORMAT_MULAW = FOURCC('u', 'l', 'a', 'w'), /* AIFC uLaw compressed */ + AIFC_FORMAT_IEEE_FLOAT32 = FOURCC('f', 'l', '3', '2'), /* AIFC IEEE float 32 bit */ + AIFC_FORMAT_IEEE_FLOAT64 = FOURCC('f', 'l', '6', '4'), /* AIFC IEEE float 64 bit */ + AIFC_FORMAT_QT_IMA_ADPCM = FOURCC('i', 'm', 'a', '4'), /* AIFC QuickTime IMA ADPCM */ +}; + +static const struct pcm_entry pcm_codecs[] = { + { AIFC_FORMAT_PCM, get_linear_pcm_codec }, + { AIFC_FORMAT_ALAW, get_itut_g711_alaw_codec }, + { AIFC_FORMAT_MULAW, get_itut_g711_mulaw_codec }, + { AIFC_FORMAT_IEEE_FLOAT32, get_ieee_float_codec }, + { AIFC_FORMAT_IEEE_FLOAT64, get_ieee_float_codec }, + { AIFC_FORMAT_QT_IMA_ADPCM, get_qt_ima_adpcm_codec }, +}; + +#define PCM_SAMPLE_SIZE (1024*2) + +static int32_t samples[PCM_SAMPLE_SIZE] IBSS_ATTR; + +static const struct pcm_codec *get_codec(uint32_t formattag) +{ + unsigned i; + for (i = 0; i < sizeof(pcm_codecs)/sizeof(pcm_codecs[0]); i++) + if (pcm_codecs[i].format_tag == formattag) + return pcm_codecs[i].get_codec(); + + return NULL; +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + struct pcm_format format; + uint32_t bytesdone, decodedsamples; + /* rockbox: comment 'set but unused' variables + uint32_t num_sample_frames = 0; + */ + size_t n; + int bufcount; + int endofstream; + unsigned char *buf; + uint8_t *aifbuf; + uint32_t offset2snd = 0; + off_t firstblockposn; /* position of the first block in file */ + bool is_aifc = false; + const struct pcm_codec *codec; + uint32_t size; + intptr_t param; + + if (codec_init()) { + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Need to save offset for later use (cleared indirectly by advance_buffer) */ + bytesdone = ci->id3->offset; + + /* assume the AIFF header is less than 1024 bytes */ + ci->seek_buffer(0); + buf = ci->request_buffer(&n, 1024); + if (n < 54) { + return CODEC_ERROR; + } + + if (memcmp(buf, "FORM", 4) != 0) + { + DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[0]); + return CODEC_ERROR; + } + if (memcmp(&buf[8], "AIFF", 4) == 0) + is_aifc = false; + else if (memcmp(&buf[8], "AIFC", 4) == 0) + is_aifc = true; + else + { + DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[8]); + return CODEC_ERROR; + } + + buf += 12; + n -= 12; + + ci->memset(&format, 0, sizeof(struct pcm_format)); + format.is_signed = true; + format.is_little_endian = false; + + decodedsamples = 0; + codec = 0; + + /* read until 'SSND' chunk, which typically is last */ + while (format.numbytes == 0 && n >= 8) + { + /* chunkSize */ + size = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]); + if (memcmp(buf, "COMM", 4) == 0) { + if ((!is_aifc && size < 18) || (is_aifc && size < 22)) + { + DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n", + (unsigned long)size, (is_aifc)?22:18); + return CODEC_ERROR; + } + /* num_channels */ + format.channels = ((buf[8]<<8)|buf[9]); + /* num_sample_frames */ + /* + num_sample_frames = ((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8) + |buf[13]); + */ + + /* sample_size */ + format.bitspersample = ((buf[14]<<8)|buf[15]); + /* sample_rate (don't use last 4 bytes, only integer fs) */ + if (buf[16] != 0x40) { + DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n"); + return CODEC_ERROR; + } + format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1; + format.samplespersec >>= (16 + 14 - buf[17]); + /* compressionType (AIFC only) */ + if (is_aifc) + { + format.formattag = (buf[26]<<24)|(buf[27]<<16)|(buf[28]<<8)|buf[29]; + + /* + * 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) + format.bitspersample = 8; + else if (format.formattag == AIFC_FORMAT_QT_IMA_ADPCM) + format.bitspersample = 4; + } + else + format.formattag = AIFC_FORMAT_PCM; + /* calc average bytes per second */ + format.avgbytespersec = format.samplespersec*format.channels*format.bitspersample/8; + } else if (memcmp(buf, "SSND", 4)==0) { + if (format.bitspersample == 0) { + DEBUGF("CODEC_ERROR: unsupported chunk order\n"); + return CODEC_ERROR; + } + /* 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]) >> 3; + if (format.blockalign == 0) + format.blockalign = format.channels * format.bitspersample >> 3; + format.numbytes = size - 8 - offset2snd; + size = 8 + offset2snd; /* advance to the beginning of data */ + } else if (is_aifc && (memcmp(buf, "FVER", 4)==0)) { + /* Format Version Chunk (AIFC only chunk) */ + /* skip this chunk */ + } else { + DEBUGF("unsupported AIFF chunk: '%c%c%c%c', size=%lu\n", + buf[0], buf[1], buf[2], buf[3], (unsigned long)size); + } + + size += 8 + (size & 0x01); /* odd chunk sizes must be padded */ + + buf += size; + if (n < size) { + DEBUGF("CODEC_ERROR: AIFF header size > 1024\n"); + return CODEC_ERROR; + } + n -= size; + } /* while 'SSND' */ + + if (format.channels == 0) { + DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n"); + return CODEC_ERROR; + } + if (format.numbytes == 0) { + DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n"); + return CODEC_ERROR; + } + + codec = get_codec(format.formattag); + if (codec == 0) + { + DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n", + (unsigned int)format.formattag); + return CODEC_ERROR; + } + + if (!codec->set_format(&format)) + { + return CODEC_ERROR; + } + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + + if (format.channels == 2) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + } else if (format.channels == 1) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + } else { + DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); + return CODEC_ERROR; + } + + if (format.samplesperblock == 0) + { + DEBUGF("CODEC_ERROR: samplesperblock is 0\n"); + return CODEC_ERROR; + } + if (format.blockalign == 0) + { + DEBUGF("CODEC_ERROR: blockalign is 0\n"); + return CODEC_ERROR; + } + + /* check chunksize */ + if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels + > PCM_SAMPLE_SIZE) + format.chunksize = (PCM_SAMPLE_SIZE / format.blockalign) * format.blockalign; + if (format.chunksize == 0) + { + DEBUGF("CODEC_ERROR: chunksize is 0\n"); + return CODEC_ERROR; + } + + firstblockposn = 1024 - n; + ci->advance_buffer(firstblockposn); + + /* make sure we're at the correct offset */ + if (bytesdone > (uint32_t) firstblockposn) { + /* Round down to previous block */ + struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, + PCM_SEEK_POS, NULL); + + if (newpos->pos > format.numbytes) + return CODEC_OK; + + if (ci->seek_buffer(firstblockposn + newpos->pos)) + { + bytesdone = newpos->pos; + decodedsamples = newpos->samples; + } + } else { + /* already where we need to be */ + bytesdone = 0; + } + + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + + /* The main decoder loop */ + endofstream = 0; + + while (!endofstream) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + /* 3rd args(read_buffer) is unnecessary in the format which AIFF supports. */ + struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL); + + if (newpos->pos > format.numbytes) + { + ci->set_elapsed(ci->id3->length); + ci->seek_complete(); + break; + } + + if (ci->seek_buffer(firstblockposn + newpos->pos)) + { + bytesdone = newpos->pos; + decodedsamples = newpos->samples; + } + + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + ci->seek_complete(); + } + aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); + + if (n == 0) + break; /* End of stream */ + + if (bytesdone + n > format.numbytes) { + n = format.numbytes - bytesdone; + endofstream = 1; + } + + if (codec->decode(aifbuf, n, samples, &bufcount) == CODEC_ERROR) + { + DEBUGF("codec error\n"); + return CODEC_ERROR; + } + + ci->pcmbuf_insert(samples, NULL, bufcount); + + ci->advance_buffer(n); + bytesdone += n; + decodedsamples += bufcount; + if (bytesdone >= format.numbytes) + endofstream = 1; + + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/aiff_enc.c b/lib/rbcodec/codecs/aiff_enc.c new file mode 100644 index 0000000000..fc44196eb0 --- /dev/null +++ b/lib/rbcodec/codecs/aiff_enc.c @@ -0,0 +1,400 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Antonius Hellmann + * + * 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 <inttypes.h> +#include "codeclib.h" + +CODEC_ENC_HEADER + +struct aiff_header +{ + uint8_t form_id[4]; /* 00h - 'FORM' */ + uint32_t form_size; /* 04h - size of file - 8 */ + uint8_t aiff_id[4]; /* 08h - 'AIFF' */ + uint8_t comm_id[4]; /* 0Ch - 'COMM' */ + int32_t comm_size; /* 10h - num_channels through sample_rate + (18) */ + int16_t num_channels; /* 14h - 1=M, 2=S, etc. */ + uint32_t num_sample_frames; /* 16h - num samples for each channel */ + int16_t sample_size; /* 1ah - 1-32 bits per sample */ + uint8_t sample_rate[10]; /* 1ch - IEEE 754 80-bit floating point */ + uint8_t ssnd_id[4]; /* 26h - "SSND" */ + int32_t ssnd_size; /* 2ah - size of chunk from offset to + end of pcm data */ + uint32_t offset; /* 2eh - data offset from end of header */ + uint32_t block_size; /* 32h - pcm data alignment */ + /* 36h */ +} __attribute__((packed)); + +#define PCM_DEPTH_BYTES 2 +#define PCM_DEPTH_BITS 16 +#define PCM_SAMP_PER_CHUNK 2048 +#define PCM_CHUNK_SIZE (PCM_SAMP_PER_CHUNK*4) + +/* Template headers */ +struct aiff_header aiff_header = +{ + { 'F', 'O', 'R', 'M' }, /* form_id */ + 0, /* form_size (*) */ + { 'A', 'I', 'F', 'F' }, /* aiff_id */ + { 'C', 'O', 'M', 'M' }, /* comm_id */ + htobe32(18), /* comm_size */ + 0, /* num_channels (*) */ + 0, /* num_sample_frames (*) */ + htobe16(PCM_DEPTH_BITS), /* sample_size */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */ + { 'S', 'S', 'N', 'D' }, /* ssnd_id */ + 0, /* ssnd_size (*) */ + htobe32(0), /* offset */ + htobe32(0), /* block_size */ +}; + +/* (*) updated when finalizing file */ + +static int num_channels IBSS_ATTR; +static int rec_mono_mode IBSS_ATTR; +static uint32_t sample_rate; +static uint32_t enc_size; +static int32_t err IBSS_ATTR; + +/* convert unsigned 32 bit value to 80-bit floating point number */ +STATICIRAM void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) + ICODE_ATTR; +STATICIRAM void uint32_h_to_ieee754_extended_be(uint8_t f[10], uint32_t l) +{ + int32_t exp; + + ci->memset(f, 0, 10); + + if (l == 0) + return; + + for (exp = 30; (l & (1ul << 31)) == 0; exp--) + l <<= 1; + + /* sign always zero - bit 79 */ + /* exponent is 0-31 (normalized: 31 - shift + 16383) - bits 64-78 */ + f[0] = 0x40; + f[1] = (uint8_t)exp; + /* mantissa is value left justified with most significant non-zero + bit stored in bit 63 - bits 0-63 */ + f[2] = (uint8_t)(l >> 24); + f[3] = (uint8_t)(l >> 16); + f[4] = (uint8_t)(l >> 8); + f[5] = (uint8_t)(l >> 0); +} /* uint32_h_to_ieee754_extended_be */ + +/* called version often - inline */ +static inline bool is_file_data_ok(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool is_file_data_ok(struct enc_file_event_data *data) +{ + return data->rec_file >= 0 && (long)data->chunk->flags >= 0; +} /* is_file_data_ok */ + +/* called version often - inline */ +static inline bool on_write_chunk(struct enc_file_event_data *data) ICODE_ATTR; +static inline bool on_write_chunk(struct enc_file_event_data *data) +{ + if (!is_file_data_ok(data)) + return false; + + if (data->chunk->enc_data == NULL) + { +#ifdef ROCKBOX_HAS_LOGF + ci->logf("aiff enc: NULL data"); +#endif + return true; + } + + if (ci->write(data->rec_file, data->chunk->enc_data, + data->chunk->enc_size) != (ssize_t)data->chunk->enc_size) + return false; + + data->num_pcm_samples += data->chunk->num_pcm; + return true; +} /* on_write_chunk */ + +static bool on_start_file(struct enc_file_event_data *data) +{ + if ((data->chunk->flags & CHUNKF_ERROR) || *data->filename == '\0') + return false; + + data->rec_file = ci->open(data->filename, O_RDWR|O_CREAT|O_TRUNC, 0666); + + if (data->rec_file < 0) + return false; + + /* reset sample count */ + data->num_pcm_samples = 0; + + /* write template headers */ + if (ci->write(data->rec_file, &aiff_header, sizeof (aiff_header)) + != sizeof (aiff_header)) + { + return false; + } + + data->new_enc_size += sizeof(aiff_header); + return true; +} /* on_start_file */ + +static bool on_end_file(struct enc_file_event_data *data) +{ + /* update template headers */ + struct aiff_header hdr; + uint32_t data_size; + + if (!is_file_data_ok(data)) + return false; + + if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || + ci->read(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr)) + { + return false; + } + + data_size = data->num_pcm_samples*num_channels*PCM_DEPTH_BYTES; + + /* 'FORM' chunk */ + hdr.form_size = htobe32(data_size + sizeof (hdr) - 8); + + /* 'COMM' chunk */ + hdr.num_channels = htobe16(num_channels); + hdr.num_sample_frames = htobe32(data->num_pcm_samples); + uint32_h_to_ieee754_extended_be(hdr.sample_rate, sample_rate); + + /* 'SSND' chunk */ + hdr.ssnd_size = htobe32(data_size + 8); + + if (ci->lseek(data->rec_file, 0, SEEK_SET) != 0 || + ci->write(data->rec_file, &hdr, sizeof (hdr)) != sizeof (hdr) || + ci->close(data->rec_file) != 0) + { + return false; + } + + data->rec_file = -1; + + return true; +} /* on_end_file */ + +STATICIRAM void enc_events_callback(enum enc_events event, void *data) + ICODE_ATTR; +STATICIRAM void enc_events_callback(enum enc_events event, void *data) +{ + switch (event) + { + case ENC_WRITE_CHUNK: + if (on_write_chunk((struct enc_file_event_data *)data)) + return; + + break; + + case ENC_START_FILE: + if (on_start_file((struct enc_file_event_data *)data)) + return; + + break; + + case ENC_END_FILE: + if (on_end_file((struct enc_file_event_data *)data)) + return; + + break; + + default: + return; + } + + /* Something failed above. Signal error back to core. */ + ((struct enc_file_event_data *)data)->chunk->flags |= CHUNKF_ERROR; +} /* enc_events_callback */ + +/* convert native pcm samples to aiff format samples */ +static inline void sample_to_mono(uint32_t **src, uint32_t **dst) +{ + int32_t lr1, lr2; + + switch(rec_mono_mode) + { + case 1: + /* mono = L */ + lr1 = *(*src)++; + lr1 = lr1 >> 16; + lr2 = *(*src)++; + lr2 = lr2 >> 16; + break; + case 2: + /* mono = R */ + lr1 = *(*src)++; + lr1 = (int16_t)lr1; + lr2 = *(*src)++; + lr2 = (int16_t)lr2; + break; + case 0: + default: + /* mono = (L+R)/2 */ + lr1 = *(*src)++; + lr1 = (int16_t)lr1 + (lr1 >> 16) + err; + err = lr1 & 1; + lr1 >>= 1; + + lr2 = *(*src)++; + lr2 = (int16_t)lr2 + (lr2 >> 16) + err; + err = lr2 & 1; + lr2 >>= 1; + break; + } + *(*dst)++ = htobe32((lr1 << 16) | (uint16_t)lr2); +} /* sample_to_mono */ + +STATICIRAM void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) ICODE_ATTR; +STATICIRAM void chunk_to_aiff_format(uint32_t *src, uint32_t *dst) +{ + if (num_channels == 1) + { + /* On big endian: + * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| + * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => + * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm| + * + * On little endian: + * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| + * |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => + * |MMMMMMMMmmmmmmmm|MMMMMMMMmmmmmmmm| + */ + uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; + + do + { + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + sample_to_mono(&src, &dst); + } + while (src < src_end); + } + else + { +#ifdef ROCKBOX_BIG_ENDIAN + /* |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| => + * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| + */ + ci->memcpy(dst, src, PCM_CHUNK_SIZE); +#else + /* |llllllllLLLLLLLL|rrrrrrrrRRRRRRRR| => + * |LLLLLLLLllllllll|RRRRRRRRrrrrrrrr| + */ + uint32_t *src_end = src + PCM_SAMP_PER_CHUNK; + + do + { + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + *dst++ = swap_odd_even32(*src++); + } + while (src < src_end); +#endif + } +} /* chunk_to_aiff_format */ + +static bool init_encoder(void) +{ + struct enc_inputs inputs; + struct enc_parameters params; + + if (ci->enc_get_inputs == NULL || + ci->enc_set_parameters == NULL || + ci->enc_get_chunk == NULL || + ci->enc_finish_chunk == NULL || + ci->enc_get_pcm_data == NULL ) + return false; + + ci->enc_get_inputs(&inputs); + + if (inputs.config->afmt != AFMT_AIFF) + return false; + + sample_rate = inputs.sample_rate; + num_channels = inputs.num_channels; + rec_mono_mode = inputs.rec_mono_mode; + err = 0; + + /* configure the buffer system */ + params.afmt = AFMT_AIFF; + enc_size = PCM_CHUNK_SIZE*inputs.num_channels / 2; + params.chunk_size = enc_size; + params.enc_sample_rate = sample_rate; + params.reserve_bytes = 0; + params.events_callback = enc_events_callback; + ci->enc_set_parameters(¶ms); + + return true; +} /* init_encoder */ + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + if (!init_encoder()) + return CODEC_ERROR; + } + else if (reason == CODEC_UNLOAD) { + /* reset parameters to initial state */ + ci->enc_set_parameters(NULL); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + /* main encoding loop */ + while (ci->get_command(NULL) != CODEC_ACTION_HALT) + { + uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); + struct enc_chunk_hdr *chunk; + + if (src == NULL) + continue; + + chunk = ci->enc_get_chunk(); + chunk->enc_size = enc_size; + chunk->num_pcm = PCM_SAMP_PER_CHUNK; + chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); + + chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data); + + ci->enc_finish_chunk(); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/alac.c b/lib/rbcodec/codecs/alac.c new file mode 100644 index 0000000000..144d796e5f --- /dev/null +++ b/lib/rbcodec/codecs/alac.c @@ -0,0 +1,146 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 Dave Chapman + * + * 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 "libm4a/m4a.h" +#include "libalac/decomp.h" + +CODEC_HEADER + +/* The maximum buffer size handled. This amount of bytes is buffered for each + * frame. */ +#define ALAC_BYTE_BUFFER_SIZE 32768 + +static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR; + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); + ci->configure(DSP_SET_SAMPLE_DEPTH, ALAC_OUTPUT_DEPTH-1); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + size_t n; + demux_res_t demux_res; + stream_t input_stream; + uint32_t samplesdone; + uint32_t elapsedtime = 0; + int samplesdecoded; + unsigned int i; + unsigned char* buffer; + alac_file alac; + intptr_t param; + + /* Clean and initialize decoder structures */ + memset(&demux_res , 0, sizeof(demux_res)); + if (codec_init()) { + LOGF("ALAC: Error initialising codec\n"); + return CODEC_ERROR; + } + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + codec_set_replaygain(ci->id3); + + ci->seek_buffer(0); + + stream_create(&input_stream,ci); + + /* Read from ci->id3->offset before calling qtmovie_read. */ + samplesdone = (uint32_t)(((uint64_t)(ci->id3->offset) * ci->id3->frequency) / + (ci->id3->bitrate*128)); + + /* if qtmovie_read returns successfully, the stream is up to + * the movie data, which can be used directly by the decoder */ + if (!qtmovie_read(&input_stream, &demux_res)) { + LOGF("ALAC: Error initialising file\n"); + return CODEC_ERROR; + } + + /* initialise the sound converter */ + alac_set_info(&alac, demux_res.codecdata); + + /* Set i for first frame, seek to desired sample position for resuming. */ + i=0; + if (samplesdone > 0) { + if (m4a_seek(&demux_res, &input_stream, samplesdone, + &samplesdone, (int*) &i)) { + elapsedtime = (samplesdone * 10) / (ci->id3->frequency / 100); + ci->set_elapsed(elapsedtime); + } else { + samplesdone = 0; + } + } + + ci->set_elapsed(elapsedtime); + + /* The main decoding loop */ + while (i < demux_res.num_sample_byte_sizes) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + /* Request the required number of bytes from the input buffer */ + buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE); + + /* Deal with any pending seek requests */ + if (action == CODEC_ACTION_SEEK_TIME) { + if (m4a_seek(&demux_res, &input_stream, + (param/10) * (ci->id3->frequency/100), + &samplesdone, (int *)&i)) { + elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); + } + ci->set_elapsed(elapsedtime); + ci->seek_complete(); + } + + /* Request the required number of bytes from the input buffer */ + buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE); + + /* Decode one block - returned samples will be host-endian */ + samplesdecoded=alac_decode_frame(&alac, buffer, outputbuffer, ci->yield); + ci->yield(); + + /* Advance codec buffer by amount of consumed bytes */ + ci->advance_buffer(alac.bytes_consumed); + + /* Output the audio */ + ci->pcmbuf_insert(outputbuffer[0], outputbuffer[1], samplesdecoded); + + /* Update the elapsed-time indicator */ + samplesdone+=samplesdecoded; + elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); + ci->set_elapsed(elapsedtime); + + i++; + } + + LOGF("ALAC: Decoded %lu samples\n",(unsigned long)samplesdone); + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/ape.c b/lib/rbcodec/codecs/ape.c new file mode 100644 index 0000000000..ed6ea21685 --- /dev/null +++ b/lib/rbcodec/codecs/ape.c @@ -0,0 +1,330 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Dave Chapman + * + * 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 <codecs/demac/libdemac/demac.h> + +CODEC_HEADER + +#define BLOCKS_PER_LOOP 1024 +#define MAX_CHANNELS 2 +#define MAX_BYTESPERSAMPLE 3 + +/* Monkey's Audio files have one seekpoint per frame. The framesize + varies between 73728 and 1179648 samples. + + At the smallest framesize, 30000 frames would be 50155 seconds of + audio - almost 14 hours. This should be enough for any file a user + would want to play in Rockbox, given the 2GB FAT filesize (and 4GB + seektable entry size) limit. + + This means the seektable is 120000 bytes, but we have a lot of + spare room in the codec buffer - the APE codec itself is small. +*/ + +#define MAX_SEEKPOINTS 30000 +static uint32_t seektablebuf[MAX_SEEKPOINTS]; + +#define INPUT_CHUNKSIZE (32*1024) + +/* 1024*4 = 4096 bytes per channel */ +static int32_t decoded0[BLOCKS_PER_LOOP] IBSS_ATTR; +static int32_t decoded1[BLOCKS_PER_LOOP] IBSS_ATTR; + +#define MAX_SUPPORTED_SEEKTABLE_SIZE 5000 + + +/* Given an ape_ctx and a sample to seek to, return the file position + to the frame containing that sample, and the number of samples to + skip in that frame. +*/ + +static bool ape_calc_seekpos(struct ape_ctx_t* ape_ctx, + uint32_t new_sample, + uint32_t* newframe, + uint32_t* filepos, + uint32_t* samplestoskip) +{ + uint32_t n; + + n = new_sample / ape_ctx->blocksperframe; + if (n >= ape_ctx->numseekpoints) + { + /* We don't have a seekpoint for that frame */ + return false; + } + + *newframe = n; + *filepos = ape_ctx->seektable[n]; + *samplestoskip = new_sample - (n * ape_ctx->blocksperframe); + + return true; +} + +/* The resume offset is a value in bytes - we need to + turn it into a frame number and samplestoskip value */ + +static void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset, + uint32_t* currentframe, uint32_t* samplesdone, + uint32_t* samplestoskip, int* firstbyte) +{ + off_t newfilepos; + int64_t framesize; + int64_t offset; + + *currentframe = 0; + *samplesdone = 0; + *samplestoskip = 0; + + while ((*currentframe < ape_ctx->totalframes) && + (*currentframe < ape_ctx->numseekpoints) && + (resume_offset > ape_ctx->seektable[*currentframe])) + { + ++*currentframe; + *samplesdone += ape_ctx->blocksperframe; + } + + if ((*currentframe > 0) && + (ape_ctx->seektable[*currentframe] > resume_offset)) { + --*currentframe; + *samplesdone -= ape_ctx->blocksperframe; + } + + newfilepos = ape_ctx->seektable[*currentframe]; + + /* APE's bytestream is weird... */ + *firstbyte = 3 - (newfilepos & 3); + newfilepos &= ~3; + + ci->seek_buffer(newfilepos); + + /* We estimate where we were in the current frame, based on the + byte offset */ + if (*currentframe < (ape_ctx->totalframes - 1)) { + framesize = ape_ctx->seektable[*currentframe+1] - ape_ctx->seektable[*currentframe]; + offset = resume_offset - ape_ctx->seektable[*currentframe]; + + *samplestoskip = (offset * ape_ctx->blocksperframe) / framesize; + } +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + struct ape_ctx_t ape_ctx; + uint32_t samplesdone; + uint32_t elapsedtime; + size_t bytesleft; + + uint32_t currentframe; + uint32_t newfilepos; + uint32_t samplestoskip; + int nblocks; + int bytesconsumed; + unsigned char* inbuffer; + uint32_t blockstodecode; + int res; + int firstbyte; + size_t resume_offset; + intptr_t param; + + if (codec_init()) { + LOGF("APE: Error initialising codec\n"); + return CODEC_ERROR; + } + + /* Remember the resume position - when the codec is opened, the + playback engine will reset it. */ + resume_offset = ci->id3->offset; + + ci->seek_buffer(0); + inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); + + /* Read the file headers to populate the ape_ctx struct */ + if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) { + LOGF("APE: Error reading header\n"); + return CODEC_ERROR; + } + + /* Initialise the seektable for this file */ + ape_ctx.seektable = seektablebuf; + ape_ctx.numseekpoints = MIN(MAX_SEEKPOINTS,ape_ctx.numseekpoints); + + ci->advance_buffer(ape_ctx.seektablefilepos); + + /* The seektable may be bigger than the guard buffer (32KB), so we + do a read() */ + ci->read_filebuf(ape_ctx.seektable, ape_ctx.numseekpoints * sizeof(uint32_t)); + +#ifdef ROCKBOX_BIG_ENDIAN + /* Byte-swap the little-endian seekpoints */ + { + uint32_t i; + + for(i = 0; i < ape_ctx.numseekpoints; i++) + ape_ctx.seektable[i] = swap32(ape_ctx.seektable[i]); + } +#endif + + /* Now advance the file position to the first frame */ + ci->advance_buffer(ape_ctx.firstframe - + (ape_ctx.seektablefilepos + + ape_ctx.numseekpoints * sizeof(uint32_t))); + + ci->configure(DSP_SWITCH_FREQUENCY, ape_ctx.samplerate); + ci->configure(DSP_SET_STEREO_MODE, ape_ctx.channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + codec_set_replaygain(ci->id3); + + /* The main decoding loop */ + + if (resume_offset) { + /* The resume offset is a value in bytes - we need to + turn it into a frame number and samplestoskip value */ + + ape_resume(&ape_ctx, resume_offset, + ¤tframe, &samplesdone, &samplestoskip, &firstbyte); + } else { + currentframe = 0; + samplesdone = 0; + samplestoskip = 0; + firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */ + } + + elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100); + ci->set_elapsed(elapsedtime); + + /* Initialise the buffer */ + inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); + + /* The main decoding loop - we decode the frames a small chunk at a time */ + while (currentframe < ape_ctx.totalframes) + { +frame_start: + /* Calculate how many blocks there are in this frame */ + if (currentframe == (ape_ctx.totalframes - 1)) + nblocks = ape_ctx.finalframeblocks; + else + nblocks = ape_ctx.blocksperframe; + + ape_ctx.currentframeblocks = nblocks; + + /* Initialise the frame decoder */ + init_frame_decoder(&ape_ctx, inbuffer, &firstbyte, &bytesconsumed); + + ci->advance_buffer(bytesconsumed); + inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); + + /* Decode the frame a chunk at a time */ + while (nblocks > 0) + { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + goto done; + + /* Deal with any pending seek requests */ + if (action == CODEC_ACTION_SEEK_TIME) + { + if (ape_calc_seekpos(&ape_ctx, + (param/10) * (ci->id3->frequency/100), + ¤tframe, + &newfilepos, + &samplestoskip)) + { + samplesdone = currentframe * ape_ctx.blocksperframe; + + /* APE's bytestream is weird... */ + firstbyte = 3 - (newfilepos & 3); + newfilepos &= ~3; + + ci->seek_buffer(newfilepos); + inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); + + elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100); + ci->set_elapsed(elapsedtime); + ci->seek_complete(); + goto frame_start; /* Sorry... */ + } + + ci->seek_complete(); + } + + blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks); + + if ((res = decode_chunk(&ape_ctx, inbuffer, &firstbyte, + &bytesconsumed, + decoded0, decoded1, + blockstodecode)) < 0) + { + /* Frame decoding error, abort */ + LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res); + return CODEC_ERROR; + } + + ci->yield(); + + if (samplestoskip > 0) { + if (samplestoskip < blockstodecode) { + ci->pcmbuf_insert(decoded0 + samplestoskip, + decoded1 + samplestoskip, + blockstodecode - samplestoskip); + samplestoskip = 0; + } else { + samplestoskip -= blockstodecode; + } + } else { + ci->pcmbuf_insert(decoded0, decoded1, blockstodecode); + } + + samplesdone += blockstodecode; + + if (!samplestoskip) { + /* Update the elapsed-time indicator */ + elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100); + ci->set_elapsed(elapsedtime); + } + + ci->advance_buffer(bytesconsumed); + inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); + + /* Decrement the block count */ + nblocks -= blockstodecode; + } + + currentframe++; + } + +done: + LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone); + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/asap.c b/lib/rbcodec/codecs/asap.c new file mode 100644 index 0000000000..19b39a44c4 --- /dev/null +++ b/lib/rbcodec/codecs/asap.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 Dominik Wenger + * + * 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 "libasap/asap.h" + +CODEC_HEADER + +#define CHUNK_SIZE (1024*2) + +static byte samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ +static ASAP_State asap IBSS_ATTR; /* asap codec state */ + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + /* Nothing to do */ + return CODEC_OK; + (void)reason; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + int n_bytes; + int song; + int duration; + char* module; + int bytesPerSample =2; + intptr_t param; + + if (codec_init()) { + DEBUGF("codec init failed\n"); + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + int bytes_done =0; + size_t filesize; + ci->seek_buffer(0); + module = ci->request_buffer(&filesize, ci->filesize); + if (!module || (size_t)filesize < (size_t)ci->filesize) + { + DEBUGF("loading error\n"); + return CODEC_ERROR; + } + + /*Init ASAP */ + if (!ASAP_Load(&asap, ci->id3->path, module, filesize)) + { + DEBUGF("%s: format not supported",ci->id3->path); + return CODEC_ERROR; + } + + /* Make use of 44.1khz */ + ci->configure(DSP_SET_FREQUENCY, 44100); + /* Sample depth is 16 bit little endian */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + /* Stereo or Mono output ? */ + if(asap.module_info->channels ==1) + { + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + bytesPerSample = 2; + } + else + { + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + bytesPerSample = 4; + } + /* reset eleapsed */ + ci->set_elapsed(0); + + song = asap.module_info->default_song; + duration = asap.module_info->durations[song]; + if (duration < 0) + duration = 180 * 1000; + + /* set id3 length, because metadata parse might not have done it */ + ci->id3->length = duration; + + ASAP_PlaySong(&asap, song, duration); + ASAP_MutePokeyChannels(&asap, 0); + + /* The main decoder loop */ + while (1) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + /* New time is ready in param */ + + /* seek to pos */ + ASAP_Seek(&asap,param); + /* update bytes_done */ + bytes_done = param*44.1*2; + /* update elapsed */ + ci->set_elapsed((bytes_done / 2) / 44.1); + /* seek ready */ + ci->seek_complete(); + } + + /* Generate a buffer full of Audio */ + #ifdef ROCKBOX_LITTLE_ENDIAN + n_bytes = ASAP_Generate(&asap, samples, sizeof(samples), ASAP_FORMAT_S16_LE); + #else + n_bytes = ASAP_Generate(&asap, samples, sizeof(samples), ASAP_FORMAT_S16_BE); + #endif + + ci->pcmbuf_insert(samples, NULL, n_bytes /bytesPerSample); + + bytes_done += n_bytes; + ci->set_elapsed((bytes_done / 2) / 44.1); + + if(n_bytes != sizeof(samples)) + break; + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/atrac3_oma.c b/lib/rbcodec/codecs/atrac3_oma.c new file mode 100644 index 0000000000..50f7c8f163 --- /dev/null +++ b/lib/rbcodec/codecs/atrac3_oma.c @@ -0,0 +1,153 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Mohamed Tarek + * + * 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 <string.h> + +#include "logf.h" +#include "codeclib.h" +#include "inttypes.h" +#include "libatrac/atrac3.h" + +CODEC_HEADER + +#define FRAMESIZE ci->id3->bytesperframe +#define BITRATE ci->id3->bitrate + +static ATRAC3Context q IBSS_ATTR; + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + /* Nothing to do */ + return CODEC_OK; + (void)reason; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + static size_t buff_size; + int datasize, res, frame_counter, total_frames, seek_frame_offset; + uint8_t *bit_buffer; + int elapsed = 0; + size_t resume_offset; + intptr_t param; + enum codec_command_action action = CODEC_ACTION_NULL; + + if (codec_init()) { + DEBUGF("codec init failed\n"); + return CODEC_ERROR; + } + + resume_offset = ci->id3->offset; + + codec_set_replaygain(ci->id3); + ci->memset(&q,0,sizeof(ATRAC3Context)); + + ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); + ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */ + ci->configure(DSP_SET_STEREO_MODE, ci->id3->channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + + ci->seek_buffer(0); + + res = atrac3_decode_init(&q, ci->id3); + if(res < 0) { + DEBUGF("failed to initialize OMA atrac decoder\n"); + return CODEC_ERROR; + } + + total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE; + frame_counter = 0; + + /* check for a mid-track resume and force a seek time accordingly */ + if(resume_offset > ci->id3->first_frame_offset) { + resume_offset -= ci->id3->first_frame_offset; + /* calculate resume_offset in frames */ + resume_offset = (int)resume_offset / FRAMESIZE; + param = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE); + action = CODEC_ACTION_SEEK_TIME; + } + else { + ci->set_elapsed(0); + ci->seek_buffer(ci->id3->first_frame_offset); + } + + /* The main decoder loop */ + while(frame_counter < total_frames) + { + if (action == CODEC_ACTION_NULL) + action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); + + if (action == CODEC_ACTION_SEEK_TIME) { + /* Do not allow seeking beyond the file's length */ + if ((unsigned) param > ci->id3->length) { + ci->set_elapsed(ci->id3->length); + ci->seek_complete(); + break; + } + + /* Seek to the start of the track */ + if (param == 0) { + elapsed = 0; + ci->set_elapsed(0); + ci->seek_buffer(ci->id3->first_frame_offset); + ci->seek_complete(); + action = CODEC_ACTION_NULL; + continue; + } + + seek_frame_offset = (param * BITRATE) / (8 * FRAMESIZE); + frame_counter = seek_frame_offset; + ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE); + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); + elapsed = param; + ci->set_elapsed(elapsed); + ci->seek_complete(); + } + + action = CODEC_ACTION_NULL; + + res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE); + + if(res != (int)FRAMESIZE) { + DEBUGF("codec error\n"); + return CODEC_ERROR; + } + + if(datasize) + ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, + q.samples_per_frame / ci->id3->channels); + + elapsed += (FRAMESIZE * 8) / BITRATE; + ci->set_elapsed(elapsed); + + ci->advance_buffer(FRAMESIZE); + frame_counter++; + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/atrac3_rm.c b/lib/rbcodec/codecs/atrac3_rm.c new file mode 100644 index 0000000000..997507425e --- /dev/null +++ b/lib/rbcodec/codecs/atrac3_rm.c @@ -0,0 +1,215 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Mohamed Tarek + * + * 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 <string.h> + +#include "logf.h" +#include "codeclib.h" +#include "inttypes.h" +#include "libatrac/atrac3.h" + +CODEC_HEADER + +static RMContext rmctx IBSS_ATTR_LARGE_IRAM; +static RMPacket pkt IBSS_ATTR_LARGE_IRAM; +static ATRAC3Context q IBSS_ATTR; + +static void init_rm(RMContext *rmctx) +{ + /* initialize the RMContext */ + memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); + + /* and atrac3 expects extadata in id3v2buf, so we shall give it that */ + memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char)); +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + /* Nothing to do */ + return CODEC_OK; + (void)reason; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + static size_t buff_size; + int datasize, res, consumed, i, time_offset; + uint8_t *bit_buffer; + uint16_t fs,sps,h; + uint32_t packet_count; + int scrambling_unit_size, num_units, elapsed = 0; + int playback_on = -1; + size_t resume_offset; + intptr_t param; + enum codec_command_action action = CODEC_ACTION_NULL; + + if (codec_init()) { + DEBUGF("codec init failed\n"); + return CODEC_ERROR; + } + + resume_offset = ci->id3->offset; + + codec_set_replaygain(ci->id3); + ci->memset(&rmctx,0,sizeof(RMContext)); + ci->memset(&pkt,0,sizeof(RMPacket)); + ci->memset(&q,0,sizeof(ATRAC3Context)); + + ci->seek_buffer(0); + init_rm(&rmctx); + + ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); + ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */ + ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + + packet_count = rmctx.nb_packets; + rmctx.audio_framesize = rmctx.block_align; + rmctx.block_align = rmctx.sub_packet_size; + fs = rmctx.audio_framesize; + sps= rmctx.block_align; + h = rmctx.sub_packet_h; + scrambling_unit_size = h * (fs + PACKET_HEADER_SIZE); + + res = atrac3_decode_init(&q, ci->id3); + if(res < 0) { + DEBUGF("failed to initialize RM atrac decoder\n"); + return CODEC_ERROR; + } + + /* check for a mid-track resume and force a seek time accordingly */ + if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { + resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; + num_units = (int)resume_offset / scrambling_unit_size; + /* put number of subpackets to skip in resume_offset */ + resume_offset /= (sps + PACKET_HEADER_SIZE); + param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); + action = CODEC_ACTION_SEEK_TIME; + } + else { + ci->set_elapsed(0); + } + + ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); + + /* The main decoder loop */ +seek_start : + while((unsigned)elapsed < rmctx.duration) + { + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); + consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); + if(consumed < 0 && playback_on != 0) { + if(playback_on == -1) { + /* Error only if packet-parsing failed and playback hadn't started */ + DEBUGF("rm_get_packet failed\n"); + return CODEC_ERROR; + } + else + return CODEC_OK; + } + + for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) + { + if (action == CODEC_ACTION_NULL) + action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + return CODEC_OK; + + if (action == CODEC_ACTION_SEEK_TIME) { + /* Do not allow seeking beyond the file's length */ + if ((unsigned) param > ci->id3->length) { + ci->set_elapsed(ci->id3->length); + ci->seek_complete(); + return CODEC_OK; + } + + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); + packet_count = rmctx.nb_packets; + rmctx.audio_pkt_cnt = 0; + rmctx.frame_number = 0; + + /* Seek to the start of the track */ + if (param == 0) { + ci->set_elapsed(0); + ci->seek_complete(); + action = CODEC_ACTION_NULL; + goto seek_start; + } + num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); + consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); + if(consumed < 0 && playback_on != 0) { + if(playback_on == -1) { + /* Error only if packet-parsing failed and playback hadn't started */ + DEBUGF("rm_get_packet failed\n"); + return CODEC_ERROR; + } + else + return CODEC_OK; + } + + packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; + rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); + while(rmctx.audiotimestamp > (unsigned) param) { + rmctx.audio_pkt_cnt = 0; + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); + consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); + packet_count += rmctx.audio_pkt_cnt; + num_units--; + } + time_offset = param - rmctx.audiotimestamp; + i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); + elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; + ci->set_elapsed(elapsed); + ci->seek_complete(); + } + + action = CODEC_ACTION_NULL; + + if(pkt.length) + res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align); + else /* indicates that there are no remaining frames */ + return CODEC_OK; + + if(res != rmctx.block_align) { + DEBUGF("codec error\n"); + return CODEC_ERROR; + } + + if(datasize) + ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, q.samples_per_frame / rmctx.nb_channels); + playback_on = 1; + elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; + ci->set_elapsed(elapsed); + rmctx.frame_number++; + } + packet_count -= rmctx.audio_pkt_cnt; + rmctx.audio_pkt_cnt = 0; + ci->advance_buffer(consumed); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/au.c b/lib/rbcodec/codecs/au.c new file mode 100644 index 0000000000..cb75c7423d --- /dev/null +++ b/lib/rbcodec/codecs/au.c @@ -0,0 +1,314 @@ +/*************************************************************************** + * __________ __ ___. + * 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 "codecs/libpcm/support_formats.h" + +CODEC_HEADER + +/* Sun Audio file (Au file format) codec + * + * References + * [1] Sun Microsystems, Inc., Header file for Audio, .au, 1992 + * URL http://www.opengroup.org/public/pubs/external/auformat.html + * [2] Wikipedia, Au file format, URL: http://en.wikipedia.org/wiki/Sun_Audio + */ + +#define PCM_SAMPLE_SIZE (1024*2) + +static int32_t samples[PCM_SAMPLE_SIZE] IBSS_ATTR; + +enum +{ + AU_FORMAT_UNSUPPORT = 0, /* unsupported format */ + AU_FORMAT_MULAW, /* G.711 MULAW */ + AU_FORMAT_PCM, /* Linear PCM */ + AU_FORMAT_IEEE_FLOAT, /* IEEE float */ + AU_FORMAT_ALAW, /* G.711 ALAW */ +}; + +static const char support_formats[9][2] = { + { AU_FORMAT_UNSUPPORT, 0 }, /* encoding */ + { AU_FORMAT_MULAW, 8 }, /* 1: G.711 MULAW */ + { AU_FORMAT_PCM, 8 }, /* 2: Linear PCM 8bit (signed) */ + { AU_FORMAT_PCM, 16 }, /* 3: Linear PCM 16bit (signed, big endian) */ + { AU_FORMAT_PCM, 24 }, /* 4: Linear PCM 24bit (signed, big endian) */ + { AU_FORMAT_PCM, 32 }, /* 5: Linear PCM 32bit (signed, big endian) */ + { AU_FORMAT_IEEE_FLOAT, 32 }, /* 6: Linear PCM float 32bit (signed, big endian) */ + { AU_FORMAT_IEEE_FLOAT, 64 }, /* 7: Linear PCM float 64bit (signed, big endian) */ + /* encoding 8 - 26 unsupported. */ + { AU_FORMAT_ALAW, 8 }, /* 27: G.711 ALAW */ +}; + +static const struct pcm_entry au_codecs[] = { + { AU_FORMAT_MULAW, get_itut_g711_mulaw_codec }, + { AU_FORMAT_PCM, get_linear_pcm_codec }, + { AU_FORMAT_IEEE_FLOAT, get_ieee_float_codec }, + { AU_FORMAT_ALAW, get_itut_g711_alaw_codec }, +}; + +#define NUM_FORMATS 4 + +static const struct pcm_codec *get_au_codec(uint32_t formattag) +{ + int i; + + for (i = 0; i < NUM_FORMATS; i++) + { + if (au_codecs[i].format_tag == formattag) + { + if (au_codecs[i].get_codec) + return au_codecs[i].get_codec(); + return 0; + } + } + return 0; +} + +static unsigned int get_be32(uint8_t *buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +static int convert_au_format(unsigned int encoding, struct pcm_format *fmt) +{ + fmt->formattag = AU_FORMAT_UNSUPPORT; + if (encoding < 8) + { + fmt->formattag = support_formats[encoding][0]; + fmt->bitspersample = support_formats[encoding][1]; + } + else if (encoding == 27) + { + fmt->formattag = support_formats[8][0]; + fmt->bitspersample = support_formats[8][1]; + } + + return fmt->formattag; +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + struct pcm_format format; + uint32_t bytesdone, decodedsamples; + size_t n; + int bufcount; + int endofstream; + unsigned char *buf; + uint8_t *aubuf; + off_t firstblockposn; /* position of the first block in file */ + const struct pcm_codec *codec; + int offset = 0; + intptr_t param; + + if (codec_init()) { + DEBUGF("codec_init() error\n"); + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Need to save offset for later use (cleared indirectly by advance_buffer) */ + bytesdone = ci->id3->offset; + + ci->memset(&format, 0, sizeof(struct pcm_format)); + format.is_signed = true; + format.is_little_endian = false; + + /* set format */ + ci->seek_buffer(0); + buf = ci->request_buffer(&n, 24); + if (n < 24 || (memcmp(buf, ".snd", 4) != 0)) + { + /* + * headerless sun audio file + * It is decoded under conditions. + * format: G.711 mu-law + * channel: mono + * frequency: 8000 kHz + */ + offset = 0; + format.formattag = AU_FORMAT_MULAW; + format.channels = 1; + format.bitspersample = 8; + format.numbytes = ci->id3->filesize; + } + else + { + /* parse header */ + + /* data offset */ + offset = get_be32(buf + 4); + if (offset < 24) + { + DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset); + return CODEC_ERROR; + } + /* data size */ + format.numbytes = get_be32(buf + 8); + if (format.numbytes == (uint32_t)0xffffffff) + format.numbytes = ci->id3->filesize - offset; + /* encoding */ + format.formattag = convert_au_format(get_be32(buf + 12), &format); + if (format.formattag == AU_FORMAT_UNSUPPORT) + { + DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf + 12)); + return CODEC_ERROR; + } + /* skip sample rate */ + format.channels = get_be32(buf + 20); + } + + /* advance to first WAVE chunk */ + ci->advance_buffer(offset); + + firstblockposn = offset; + + decodedsamples = 0; + codec = 0; + + /* get codec */ + codec = get_au_codec(format.formattag); + if (!codec) + { + DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format.formattag); + return CODEC_ERROR; + } + + if (!codec->set_format(&format)) + { + return CODEC_ERROR; + } + + if (format.numbytes == 0) { + DEBUGF("CODEC_ERROR: data size is 0\n"); + return CODEC_ERROR; + } + + /* check chunksize */ + if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels + > PCM_SAMPLE_SIZE) + format.chunksize = (PCM_SAMPLE_SIZE / format.blockalign) * format.blockalign; + if (format.chunksize == 0) + { + DEBUGF("CODEC_ERROR: chunksize is 0\n"); + return CODEC_ERROR; + } + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + if (format.channels == 2) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + } else if (format.channels == 1) { + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + } else { + DEBUGF("CODEC_ERROR: more than 2 channels\n"); + return CODEC_ERROR; + } + + /* make sure we're at the correct offset */ + if (bytesdone > (uint32_t) firstblockposn) { + /* Round down to previous block */ + struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, + PCM_SEEK_POS, NULL); + + if (newpos->pos > format.numbytes) + goto done; + if (ci->seek_buffer(firstblockposn + newpos->pos)) + { + bytesdone = newpos->pos; + decodedsamples = newpos->samples; + } + } else { + /* already where we need to be */ + bytesdone = 0; + } + + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + + /* The main decoder loop */ + endofstream = 0; + + while (!endofstream) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + /* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */ + struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL); + + if (newpos->pos > format.numbytes) + { + ci->set_elapsed(ci->id3->length); + ci->seek_complete(); + break; + } + + if (ci->seek_buffer(firstblockposn + newpos->pos)) + { + bytesdone = newpos->pos; + decodedsamples = newpos->samples; + } + + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + ci->seek_complete(); + } + + aubuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); + if (n == 0) + break; /* End of stream */ + if (bytesdone + n > format.numbytes) { + n = format.numbytes - bytesdone; + endofstream = 1; + } + + if (codec->decode(aubuf, n, samples, &bufcount) == CODEC_ERROR) + { + DEBUGF("codec error\n"); + return CODEC_ERROR; + } + + ci->pcmbuf_insert(samples, NULL, bufcount); + ci->advance_buffer(n); + bytesdone += n; + decodedsamples += bufcount; + + if (bytesdone >= format.numbytes) + endofstream = 1; + ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); + } + +done: + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/ay.c b/lib/rbcodec/codecs/ay.c new file mode 100644 index 0000000000..b11ad84294 --- /dev/null +++ b/lib/rbcodec/codecs/ay.c @@ -0,0 +1,137 @@ + +/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ + +#include <codecs/lib/codeclib.h> +#include "libgme/ay_emu.h" + +CODEC_HEADER + +/* Maximum number of bytes to process in one iteration */ +#define CHUNK_SIZE (1024*2) + +static int16_t samples[CHUNK_SIZE] IBSS_ATTR; +static struct Ay_Emu ay_emu; + +/****************** rockbox interface ******************/ + +static void set_codec_track(int t, int multitrack) { + Ay_start_track(&ay_emu, t); + + /* for loop mode we disable track limits */ + if (!ci->loop_track()) { + Track_set_fade(&ay_emu, Track_get_length( &ay_emu, t ) - 4000, 4000); + } + if (multitrack) ci->set_elapsed(t*1000); /* t is track no to display */ + else ci->set_elapsed(0); +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* we only render 16 bits */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + + /* 44 Khz, Interleaved stereo */ + ci->configure(DSP_SET_FREQUENCY, 44100); + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + + Ay_init(&ay_emu); + Ay_set_sample_rate(&ay_emu, 44100); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + blargg_err_t err; + uint8_t *buf; + size_t n; + int track, is_multitrack; + intptr_t param; + uint32_t elapsed_time; + + /* reset values */ + track = is_multitrack = 0; + elapsed_time = 0; + + DEBUGF("AY: next_track\n"); + if (codec_init()) { + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Read the entire file */ + DEBUGF("AY: request file\n"); + ci->seek_buffer(0); + buf = ci->request_buffer(&n, ci->filesize); + if (!buf || n < (size_t)ci->filesize) { + DEBUGF("AY: file load failed\n"); + return CODEC_ERROR; + } + + if ((err = Ay_load_mem(&ay_emu, buf, ci->filesize))) { + DEBUGF("AY: Ay_load_mem failed (%s)\n", err); + return CODEC_ERROR; + } + + /* Update internal track count */ + if (ay_emu.m3u.size > 0) + ay_emu.track_count = ay_emu.m3u.size; + + /* Check if file has multiple tracks */ + if (ay_emu.track_count > 1) { + is_multitrack = 1; + } + +next_track: + set_codec_track(track, is_multitrack); + + /* The main decoder loop */ + while (1) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + if (is_multitrack) { + track = param/1000; + ci->seek_complete(); + if (track >= ay_emu.track_count) break; + goto next_track; + } + + ci->set_elapsed(param); + elapsed_time = param; + Track_seek(&ay_emu, param); + ci->seek_complete(); + + /* Set fade again */ + if (!ci->loop_track()) { + Track_set_fade(&ay_emu, Track_get_length( &ay_emu, track ) - 4000, 4000); + } + } + + /* Generate audio buffer */ + err = Ay_play(&ay_emu, CHUNK_SIZE, samples); + if (err || Track_ended(&ay_emu)) { + track++; + if (track >= ay_emu.track_count) break; + goto next_track; + } + + ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1); + + /* Set elapsed time for one track files */ + if (!is_multitrack) { + elapsed_time += (CHUNK_SIZE / 2) * 10 / 441; + ci->set_elapsed(elapsed_time); + } + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/codec_crt0.c b/lib/rbcodec/codecs/codec_crt0.c new file mode 100644 index 0000000000..e3c3321e54 --- /dev/null +++ b/lib/rbcodec/codecs/codec_crt0.c @@ -0,0 +1,74 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Tomasz Malesinski + * + * 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 "config.h" +#include "codecs.h" + +struct codec_api *ci DATA_ATTR; + +extern unsigned char plugin_bss_start[]; +extern unsigned char plugin_end_addr[]; + +/* stub, the entry point is called via its reference in __header to + * avoid warning with certain compilers */ +int _start(void) {return 0;} + +enum codec_status codec_start(enum codec_entry_call_reason reason) +{ +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) + if (reason == CODEC_LOAD) + { +#ifdef USE_IRAM + extern char iramcopy[], iramstart[], iramend[], iedata[], iend[]; + size_t iram_size = iramend - iramstart; + size_t ibss_size = iend - iedata; + if (iram_size > 0 || ibss_size > 0) + { + ci->memcpy(iramstart, iramcopy, iram_size); + ci->memset(iedata, 0, ibss_size); + /* make the icache (if it exists) up to date with the new code */ + ci->commit_discard_idcache(); + /* barrier to prevent reordering iram copy and BSS clearing, + * because the BSS segment alias the IRAM copy. + */ + asm volatile ("" ::: "memory"); + } +#endif /* PLUGIN_USE_IRAM */ + ci->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start); + /* Some parts of bss may be used via a no-cache alias (at least + * portalplayer has this). If we don't clear the cache, those aliases + * may read garbage */ + ci->commit_dcache(); + } +#endif /* CONFIG_PLATFORM */ + + /* Note: If for any reason codec_main would not be called with CODEC_LOAD + * because the above code failed then it must not be ever be called with + * any other value and some strategy to avoid doing so must be conceived */ + return codec_main(reason); +} + +#if defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE) +void __attribute__((naked)) __div0(void) +{ + asm volatile("bx %0" : : "r"(ci->__div0)); +} +#endif diff --git a/lib/rbcodec/codecs/codecs.h b/lib/rbcodec/codecs/codecs.h new file mode 100644 index 0000000000..bad8cdd469 --- /dev/null +++ b/lib/rbcodec/codecs/codecs.h @@ -0,0 +1,291 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 Björn Stenberg + * + * 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 _CODECS_H_ +#define _CODECS_H_ + +/* instruct simulator code to not redefine any symbols when compiling codecs. + (the CODEC macro is defined in codecs.make) */ +#ifdef CODEC +#define NO_REDEFINES_PLEASE +#endif + +#include <stdbool.h> +#include <stdlib.h> +#include "strlcpy.h" +#include "config.h" +#include "system.h" +#include "metadata.h" +#include "audio.h" +#ifdef RB_PROFILE +#include "profile.h" +#include "thread.h" +#endif +#if (CONFIG_CODEC == SWCODEC) +#ifdef HAVE_RECORDING +#include "pcm_record.h" +#endif +#include "dsp.h" +#include "dsp-util.h" +#endif + +#include "gcc_extensions.h" +#include "load_code.h" + +#ifdef CODEC +#if defined(DEBUG) || defined(SIMULATOR) +#undef DEBUGF +#define DEBUGF ci->debugf +#undef LDEBUGF +#define LDEBUGF ci->debugf +#else +#define DEBUGF(...) +#define LDEBUGF(...) +#endif + +#ifdef ROCKBOX_HAS_LOGF +#undef LOGF +#define LOGF ci->logf +#else +#define LOGF(...) +#endif + +#endif + +/* magic for normal codecs */ +#define CODEC_MAGIC 0x52434F44 /* RCOD */ +/* magic for encoder codecs */ +#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ + +/* increase this every time the api struct changes */ +#define CODEC_API_VERSION 44 + +/* update this to latest version if a change to the api struct breaks + backwards compatibility (and please take the opportunity to sort in any + new function which are "waiting" at the end of the function table) */ +#define CODEC_MIN_API_VERSION 43 + +/* reasons for calling codec main entrypoint */ +enum codec_entry_call_reason { + CODEC_LOAD = 0, + CODEC_UNLOAD +}; + +/* codec return codes */ +enum codec_status { + CODEC_OK = 0, + CODEC_ERROR = -1, +}; + +/* codec command action codes */ +enum codec_command_action { + CODEC_ACTION_HALT = -1, + CODEC_ACTION_NULL = 0, + CODEC_ACTION_SEEK_TIME = 1, +}; + +/* NOTE: To support backwards compatibility, only add new functions at + the end of the structure. Every time you add a new function, + remember to increase CODEC_API_VERSION. If you make changes to the + existing APIs then also update CODEC_MIN_API_VERSION to current + version + */ +struct codec_api { + off_t filesize; /* Total file length */ + off_t curpos; /* Current buffer position */ + + struct mp3entry *id3; /* TAG metadata pointer */ + int audio_hid; /* Current audio handle */ + + /* The dsp instance to be used for audio output */ + struct dsp_config *dsp; + + /* Returns buffer to malloc array. Only codeclib should need this. */ + void* (*codec_get_buffer)(size_t *size); + /* Insert PCM data into audio buffer for playback. Playback will start + automatically. */ + void (*pcmbuf_insert)(const void *ch1, const void *ch2, int count); + /* Set song position in WPS (value in ms). */ + void (*set_elapsed)(unsigned long value); + + /* Read next <size> amount bytes from file buffer to <ptr>. + Will return number of bytes read or 0 if end of file. */ + size_t (*read_filebuf)(void *ptr, size_t size); + /* Request pointer to file buffer which can be used to read + <realsize> amount of data. <reqsize> tells the buffer system + how much data it should try to allocate. If <realsize> is 0, + end of file is reached. */ + void* (*request_buffer)(size_t *realsize, size_t reqsize); + /* Advance file buffer position by <amount> amount of bytes. */ + void (*advance_buffer)(size_t amount); + /* Seek file buffer to position <newpos> beginning of file. */ + bool (*seek_buffer)(size_t newpos); + /* Codec should call this function when it has done the seeking. */ + void (*seek_complete)(void); + /* Update the current position */ + void (*set_offset)(size_t value); + /* Configure different codec buffer parameters. */ + void (*configure)(int setting, intptr_t value); + /* Obtain command action on what to do next */ + enum codec_command_action (*get_command)(intptr_t *param); + /* Determine whether the track should be looped, if applicable. */ + bool (*loop_track)(void); + + /* kernel/ system */ +#if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE + void (*__div0)(void); +#endif + unsigned (*sleep)(unsigned ticks); + void (*yield)(void); + +#if NUM_CORES > 1 + unsigned int + (*create_thread)(void (*function)(void), void* stack, + size_t stack_size, unsigned flags, const char *name + IF_PRIO(, int priority) + IF_COP(, unsigned int core)); + + void (*thread_thaw)(unsigned int thread_id); + void (*thread_wait)(unsigned int thread_id); + void (*semaphore_init)(struct semaphore *s, int max, int start); + int (*semaphore_wait)(struct semaphore *s, int timeout); + void (*semaphore_release)(struct semaphore *s); +#endif /* NUM_CORES */ + + void (*commit_dcache)(void); + void (*commit_discard_dcache)(void); + + /* strings and memory */ + char* (*strcpy)(char *dst, const char *src); + size_t (*strlen)(const char *str); + int (*strcmp)(const char *, const char *); + char *(*strcat)(char *s1, const char *s2); + void* (*memset)(void *dst, int c, size_t length); + void* (*memcpy)(void *out, const void *in, size_t n); + void* (*memmove)(void *out, const void *in, size_t n); + int (*memcmp)(const void *s1, const void *s2, size_t n); + void *(*memchr)(const void *s1, int c, size_t n); + +#if defined(DEBUG) || defined(SIMULATOR) + void (*debugf)(const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2); +#endif +#ifdef ROCKBOX_HAS_LOGF + void (*logf)(const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2); +#endif + + /* Tremor requires qsort */ + void (*qsort)(void *base, size_t nmemb, size_t size, + int(*compar)(const void *, const void *)); + +#ifdef RB_PROFILE + void (*profile_thread)(void); + void (*profstop)(void); + void (*profile_func_enter)(void *this_fn, void *call_site); + void (*profile_func_exit)(void *this_fn, void *call_site); +#endif + +#ifdef HAVE_RECORDING + void (*enc_get_inputs)(struct enc_inputs *inputs); + void (*enc_set_parameters)(struct enc_parameters *params); + struct enc_chunk_hdr * (*enc_get_chunk)(void); + void (*enc_finish_chunk)(void); + unsigned char * (*enc_get_pcm_data)(size_t size); + size_t (*enc_unget_pcm_data)(size_t size); + + /* file */ + int (*open)(const char* pathname, int flags, ...); + int (*close)(int fd); + ssize_t (*read)(int fd, void* buf, size_t count); + off_t (*lseek)(int fd, off_t offset, int whence); + ssize_t (*write)(int fd, const void* buf, size_t count); + int (*round_value_to_list32)(unsigned long value, + const unsigned long list[], + int count, + bool signd); +#endif + + /* new stuff at the end, sort into place next time + the API gets incompatible */ + void (*commit_discard_idcache)(void); +}; + +/* codec header */ +struct codec_header { + struct lc_header lc_hdr; /* must be first */ + enum codec_status(*entry_point)(enum codec_entry_call_reason reason); + enum codec_status(*run_proc)(void); + struct codec_api **api; +}; + +#ifdef CODEC +#if (CONFIG_PLATFORM & PLATFORM_NATIVE) +/* plugin_* is correct, codecs use the plugin linker script */ +extern unsigned char plugin_start_addr[]; +extern unsigned char plugin_end_addr[]; +/* decoders */ +#define CODEC_HEADER \ + const struct codec_header __header \ + __attribute__ ((section (".header")))= { \ + { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ + plugin_start_addr, plugin_end_addr }, codec_start, \ + codec_run, &ci }; +/* encoders */ +#define CODEC_ENC_HEADER \ + const struct codec_header __header \ + __attribute__ ((section (".header")))= { \ + { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ + plugin_start_addr, plugin_end_addr }, codec_start, \ + codec_run, &ci }; + +#else /* def SIMULATOR */ +/* decoders */ +#define CODEC_HEADER \ + const struct codec_header __header \ + __attribute__((visibility("default"))) = { \ + { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ + codec_start, codec_run, &ci }; +/* encoders */ +#define CODEC_ENC_HEADER \ + const struct codec_header __header = { \ + { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ + codec_start, codec_run, &ci }; +#endif /* SIMULATOR */ +#endif /* CODEC */ + +/* create full codec path from root filenames in audio_formats[] + assumes buffer size is MAX_PATH */ +void codec_get_full_path(char *path, const char *codec_root_fn); + +/* Returns pointer to and size of free codec RAM */ +void *codec_get_buffer_callback(size_t *size); + +/* defined by the codec loader (codec.c) */ +int codec_load_buf(int hid, struct codec_api *api); +int codec_load_file(const char* codec, struct codec_api *api); +int codec_run_proc(void); +int codec_halt(void); +int codec_close(void); + +/* defined by the codec */ +enum codec_status codec_start(enum codec_entry_call_reason reason); +enum codec_status codec_main(enum codec_entry_call_reason reason); +enum codec_status codec_run(void); + +#endif /* _CODECS_H_ */ diff --git a/lib/rbcodec/codecs/codecs.make b/lib/rbcodec/codecs/codecs.make new file mode 100644 index 0000000000..919aef2024 --- /dev/null +++ b/lib/rbcodec/codecs/codecs.make @@ -0,0 +1,206 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +CODECDIR = $(RBCODEC_BLD)/codecs +CODECS_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/SOURCES) +OTHER_SRC += $(CODECS_SRC) + +CODECS := $(CODECS_SRC:.c=.codec) +CODECS := $(subst $(RBCODECLIB_DIR),$(RBCODEC_BLD),$(CODECS)) + +# the codec helper library +include $(RBCODECLIB_DIR)/codecs/lib/libcodec.make +OTHER_INC += -I$(RBCODECLIB_DIR)/codecs/lib + +# extra libraries +CODEC_LIBS := $(EXTRA_LIBS) $(CODECLIB) + +# the codec libraries +include $(RBCODECLIB_DIR)/codecs/demac/libdemac.make +include $(RBCODECLIB_DIR)/codecs/liba52/liba52.make +include $(RBCODECLIB_DIR)/codecs/libalac/libalac.make +include $(RBCODECLIB_DIR)/codecs/libasap/libasap.make +include $(RBCODECLIB_DIR)/codecs/libasf/libasf.make +include $(RBCODECLIB_DIR)/codecs/libfaad/libfaad.make +include $(RBCODECLIB_DIR)/codecs/libffmpegFLAC/libffmpegFLAC.make +include $(RBCODECLIB_DIR)/codecs/libm4a/libm4a.make +include $(RBCODECLIB_DIR)/codecs/libmad/libmad.make +include $(RBCODECLIB_DIR)/codecs/libmusepack/libmusepack.make +include $(RBCODECLIB_DIR)/codecs/libspc/libspc.make +include $(RBCODECLIB_DIR)/codecs/libspeex/libspeex.make +include $(RBCODECLIB_DIR)/codecs/libtremor/libtremor.make +include $(RBCODECLIB_DIR)/codecs/libwavpack/libwavpack.make +include $(RBCODECLIB_DIR)/codecs/libwma/libwma.make +include $(RBCODECLIB_DIR)/codecs/libwmapro/libwmapro.make +include $(RBCODECLIB_DIR)/codecs/libcook/libcook.make +include $(RBCODECLIB_DIR)/codecs/librm/librm.make +include $(RBCODECLIB_DIR)/codecs/libatrac/libatrac.make +include $(RBCODECLIB_DIR)/codecs/libpcm/libpcm.make +include $(RBCODECLIB_DIR)/codecs/libtta/libtta.make +include $(RBCODECLIB_DIR)/codecs/libgme/libay.make +include $(RBCODECLIB_DIR)/codecs/libgme/libgbs.make +include $(RBCODECLIB_DIR)/codecs/libgme/libhes.make +include $(RBCODECLIB_DIR)/codecs/libgme/libnsf.make +include $(RBCODECLIB_DIR)/codecs/libgme/libsgc.make +include $(RBCODECLIB_DIR)/codecs/libgme/libvgm.make +include $(RBCODECLIB_DIR)/codecs/libgme/libkss.make +include $(RBCODECLIB_DIR)/codecs/libgme/libemu2413.make + +# compile flags for codecs +CODECFLAGS = $(CFLAGS) $(RBCODEC_CFLAGS) -fstrict-aliasing \ + -I$(RBCODECLIB_DIR)/codecs -I$(RBCODECLIB_DIR)/codecs/lib -DCODEC + +# set CODECFLAGS per codec lib, since gcc takes the last -Ox and the last +# in a -ffoo -fno-foo pair, there is no need to filter them out +$(A52LIB) : CODECFLAGS += -O1 +$(ALACLIB) : CODECFLAGS += -O1 +$(ASAPLIB) : CODECFLAGS += -O1 +$(ASFLIB) : CODECFLAGS += -O2 +$(ATRACLIB) : CODECFLAGS += -O1 +$(AYLIB) : CODECFLAGS += -O2 +$(COOKLIB): CODECFLAGS += -O1 +$(DEMACLIB) : CODECFLAGS += -O3 +$(FAADLIB) : CODECFLAGS += -O2 +$(FFMPEGFLACLIB) : CODECFLAGS += -O2 +$(GBSLIB) : CODECFLAGS += -O2 +$(HESLIB) : CODECFLAGS += -O2 +$(KSSLIB) : CODECFLAGS += -O2 +$(M4ALIB) : CODECFLAGS += -O3 +$(MUSEPACKLIB) : CODECFLAGS += -O1 +$(NSFLIB) : CODECFLAGS += -O2 +$(PCMSLIB) : CODECFLAGS += -O1 +$(RMLIB) : CODECFLAGS += -O3 +$(SGCLIB) : CODECFLAGS += -O2 +$(SPCLIB) : CODECFLAGS += -O1 +$(TREMORLIB) : CODECFLAGS += -O2 +$(TTALIB) : CODECFLAGS += -O2 +$(VGMLIB) : CODECFLAGS += -O2 +$(EMU2413LIB) : CODECFLAGS += -O3 +$(WAVPACKLIB) : CODECFLAGS += -O1 +$(WMALIB) : CODECFLAGS += -O2 +$(WMAPROLIB) : CODECFLAGS += -O1 +$(WMAVOICELIB) : CODECFLAGS += -O1 + +# fine-tuning of CODECFLAGS per cpu arch +ifeq ($(ARCH),arch_arm) + # redo per arm generation + $(ALACLIB) : CODECFLAGS += -O2 + $(AYLIB) : CODECFLAGS += -O1 + $(GBSLIB) : CODECFLAGS += -O1 + $(HESLIB) : CODECFLAGS += -O1 + $(KSSLIB) : CODECFLAGS += -O1 + $(NSFLIB) : CODECFLAGS += -O1 + $(SGCLIB) : CODECFLAGS += -O1 + $(VGMLIB) : CODECFLAGS += -O1 + $(EMU2413LIB) : CODECFLAGS += -O3 + $(WAVPACKLIB) : CODECFLAGS += -O3 +else ifeq ($(ARCH),arch_m68k) + $(A52LIB) : CODECFLAGS += -O2 + $(ASFLIB) : CODECFLAGS += -O3 + $(ATRACLIB) : CODECFLAGS += -O2 + $(COOKLIB): CODECFLAGS += -O2 + $(DEMACLIB) : CODECFLAGS += -O2 + $(SPCLIB) : CODECFLAGS += -O3 + $(WMAPROLIB) : CODECFLAGS += -O3 + $(WMAVOICELIB) : CODECFLAGS += -O2 +endif + +ifeq ($(MEMORYSIZE),2) + $(ASFLIB) : CODECFLAGS += -Os + $(WMALIB) : CODECFLAGS += -Os +endif + +ifndef APP_TYPE + CONFIGFILE := $(FIRMDIR)/export/config/$(MODELNAME).h + CODEC_LDS := $(APPSDIR)/plugins/plugin.lds # codecs and plugins use same file + CODECLINK_LDS := $(CODECDIR)/codec.link +endif + +CODEC_CRT0 := $(CODECDIR)/codec_crt0.o + +$(CODECS): $(CODEC_CRT0) $(CODECLINK_LDS) + +$(CODECLINK_LDS): $(CODEC_LDS) $(CONFIGFILE) + $(call PRINTS,PP $(@F)) + $(shell mkdir -p $(dir $@)) + $(call preprocess2file, $<, $@, -DCODEC) + +# codec/library dependencies +$(CODECDIR)/spc.codec : $(CODECDIR)/libspc.a +$(CODECDIR)/mpa.codec : $(CODECDIR)/libmad.a +$(CODECDIR)/a52.codec : $(CODECDIR)/liba52.a +$(CODECDIR)/flac.codec : $(CODECDIR)/libffmpegFLAC.a +$(CODECDIR)/vorbis.codec : $(CODECDIR)/libtremor.a +$(CODECDIR)/speex.codec : $(CODECDIR)/libspeex.a +$(CODECDIR)/mpc.codec : $(CODECDIR)/libmusepack.a +$(CODECDIR)/wavpack.codec : $(CODECDIR)/libwavpack.a +$(CODECDIR)/alac.codec : $(CODECDIR)/libalac.a $(CODECDIR)/libm4a.a +$(CODECDIR)/aac.codec : $(CODECDIR)/libfaad.a $(CODECDIR)/libm4a.a +$(CODECDIR)/shorten.codec : $(CODECDIR)/libffmpegFLAC.a +$(CODECDIR)/ape-pre.map : $(CODECDIR)/libdemac-pre.a +$(CODECDIR)/ape.codec : $(CODECDIR)/libdemac.a +$(CODECDIR)/wma.codec : $(CODECDIR)/libwma.a $(CODECDIR)/libasf.a +$(CODECDIR)/wmapro.codec : $(CODECDIR)/libwmapro.a $(CODECDIR)/libasf.a +$(CODECDIR)/wavpack_enc.codec: $(CODECDIR)/libwavpack.a +$(CODECDIR)/asap.codec : $(CODECDIR)/libasap.a +$(CODECDIR)/cook.codec : $(CODECDIR)/libcook.a $(CODECDIR)/librm.a +$(CODECDIR)/raac.codec : $(CODECDIR)/libfaad.a $(CODECDIR)/librm.a +$(CODECDIR)/a52_rm.codec : $(CODECDIR)/liba52.a $(CODECDIR)/librm.a +$(CODECDIR)/atrac3_rm.codec : $(CODECDIR)/libatrac.a $(CODECDIR)/librm.a +$(CODECDIR)/atrac3_oma.codec : $(CODECDIR)/libatrac.a +$(CODECDIR)/aiff.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/wav.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/smaf.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/au.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/vox.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/wav64.codec : $(CODECDIR)/libpcm.a +$(CODECDIR)/tta.codec : $(CODECDIR)/libtta.a +$(CODECDIR)/ay.codec : $(CODECDIR)/libay.a +$(CODECDIR)/gbs.codec : $(CODECDIR)/libgbs.a +$(CODECDIR)/hes.codec : $(CODECDIR)/libhes.a +$(CODECDIR)/nsf.codec : $(CODECDIR)/libnsf.a $(CODECDIR)/libemu2413.a +$(CODECDIR)/sgc.codec : $(CODECDIR)/libsgc.a $(CODECDIR)/libemu2413.a +$(CODECDIR)/vgm.codec : $(CODECDIR)/libvgm.a $(CODECDIR)/libemu2413.a +$(CODECDIR)/kss.codec : $(CODECDIR)/libkss.a $(CODECDIR)/libemu2413.a + +$(CODECS): $(CODEC_LIBS) # this must be last in codec dependency list + +# pattern rule for compiling codecs +$(CODECDIR)/%.o: $(RBCODECLIB_DIR)/codecs/%.c + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) \ + -I$(dir $<) $(CODECFLAGS) -c $< -o $@ + +# pattern rule for compiling codecs +$(CODECDIR)/%.o: $(RBCODECLIB_DIR)/codecs/%.S + $(SILENT)mkdir -p $(dir $@) + $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) \ + -I$(dir $<) $(CODECFLAGS) $(ASMFLAGS) -c $< -o $@ + +ifdef APP_TYPE + CODECLDFLAGS = $(SHARED_LDFLAG) -Wl,--gc-sections -Wl,-Map,$(CODECDIR)/$*.map + CODECFLAGS += $(SHARED_CFLAGS) # <-- from Makefile +else + CODECLDFLAGS = -T$(CODECLINK_LDS) -Wl,--gc-sections -Wl,-Map,$(CODECDIR)/$*.map + CODECFLAGS += -UDEBUG -DNDEBUG +endif +CODECLDFLAGS += $(GLOBAL_LDOPTS) + +$(CODECDIR)/%-pre.map: $(CODEC_CRT0) $(CODECLINK_LDS) $(CODECDIR)/%.o $(CODECS_LIBS) + $(call PRINTS,LD $(@F))$(CC) $(CODECFLAGS) -o $(CODECDIR)/$*-pre.elf \ + $(filter %.o, $^) \ + $(filter-out $(CODECLIB),$(filter %.a, $+)) $(CODECLIB) \ + -lgcc $(subst .map,-pre.map,$(CODECLDFLAGS)) + +$(CODECDIR)/%.codec: $(CODECDIR)/%.o + $(call PRINTS,LD $(@F))$(CC) $(CODECFLAGS) -o $(CODECDIR)/$*.elf \ + $(filter %.o, $^) \ + $(filter %.a, $+) \ + -lgcc $(CODECLDFLAGS) + $(SILENT)$(call objcopy,$(CODECDIR)/$*.elf,$@) diff --git a/lib/rbcodec/codecs/cook.c b/lib/rbcodec/codecs/cook.c new file mode 100644 index 0000000000..55188aad36 --- /dev/null +++ b/lib/rbcodec/codecs/cook.c @@ -0,0 +1,202 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 Mohamed Tarek + * + * 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 <string.h> + +#include "logf.h" +#include "codeclib.h" +#include "inttypes.h" +#include "libcook/cook.h" + +CODEC_HEADER + +static RMContext rmctx IBSS_ATTR_COOK_LARGE_IRAM; +static RMPacket pkt IBSS_ATTR_COOK_LARGE_IRAM; +static COOKContext q IBSS_ATTR; +static int32_t rm_outbuf[2048] IBSS_ATTR_COOK_LARGE_IRAM MEM_ALIGN_ATTR; + +static void init_rm(RMContext *rmctx) +{ + memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + /* Nothing to do */ + return CODEC_OK; + (void)reason; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + static size_t buff_size; + int datasize, res, consumed, i, time_offset; + uint8_t *bit_buffer; + uint16_t fs,sps,h; + uint32_t packet_count; + int scrambling_unit_size, num_units; + size_t resume_offset; + intptr_t param = 0; + enum codec_command_action action = CODEC_ACTION_NULL; + + if (codec_init()) { + DEBUGF("codec init failed\n"); + return CODEC_ERROR; + } + + resume_offset = ci->id3->offset; + + codec_set_replaygain(ci->id3); + ci->memset(&rmctx,0,sizeof(RMContext)); + ci->memset(&pkt,0,sizeof(RMPacket)); + ci->memset(&q,0,sizeof(COOKContext)); + + ci->seek_buffer(0); + + init_rm(&rmctx); + + ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); + /* cook's sample representation is 21.11 + * DSP_SET_SAMPLE_DEPTH = 11 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 26 */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 26); + ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + + packet_count = rmctx.nb_packets; + rmctx.audio_framesize = rmctx.block_align; + rmctx.block_align = rmctx.sub_packet_size; + fs = rmctx.audio_framesize; + sps= rmctx.block_align; + h = rmctx.sub_packet_h; + scrambling_unit_size = h * (fs + PACKET_HEADER_SIZE); + + res =cook_decode_init(&rmctx, &q); + if(res < 0) { + DEBUGF("failed to initialize cook decoder\n"); + return CODEC_ERROR; + } + + /* check for a mid-track resume and force a seek time accordingly */ + if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { + resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; + num_units = (int)resume_offset / scrambling_unit_size; + /* put number of subpackets to skip in resume_offset */ + resume_offset /= (sps + PACKET_HEADER_SIZE); + param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); + action = CODEC_ACTION_SEEK_TIME; + } + else { + ci->set_elapsed(0); + } + + ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); + + /* The main decoder loop */ +seek_start : + while(packet_count) + { + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); + consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); + if(consumed < 0) { + DEBUGF("rm_get_packet failed\n"); + return CODEC_ERROR; + } + + for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) + { + if (action == CODEC_ACTION_NULL) + action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + return CODEC_OK; + + if (action == CODEC_ACTION_SEEK_TIME) { + /* Do not allow seeking beyond the file's length */ + if ((unsigned) param > ci->id3->length) { + ci->set_elapsed(ci->id3->length); + ci->seek_complete(); + return CODEC_OK; + } + + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); + packet_count = rmctx.nb_packets; + rmctx.audio_pkt_cnt = 0; + rmctx.frame_number = 0; + + /* Seek to the start of the track */ + if (param == 0) { + ci->set_elapsed(0); + ci->seek_complete(); + action = CODEC_ACTION_NULL; + goto seek_start; + } + num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); + consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); + if(consumed < 0) { + DEBUGF("rm_get_packet failed\n"); + ci->seek_complete(); + return CODEC_ERROR; + } + packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; + rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); + while(rmctx.audiotimestamp > (unsigned) param) { + rmctx.audio_pkt_cnt = 0; + ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); + bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); + consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); + packet_count += rmctx.audio_pkt_cnt; + num_units--; + } + time_offset = param - rmctx.audiotimestamp; + i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); + ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); + ci->seek_complete(); + } + + action = CODEC_ACTION_NULL; + + res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align); + rmctx.frame_number++; + + /* skip the first two frames; no valid audio */ + if(rmctx.frame_number < 3) continue; + + if(res != rmctx.block_align) { + DEBUGF("codec error\n"); + return CODEC_ERROR; + } + + ci->pcmbuf_insert(rm_outbuf, + rm_outbuf+q.samples_per_channel, + q.samples_per_channel); + ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); + } + packet_count -= rmctx.audio_pkt_cnt; + rmctx.audio_pkt_cnt = 0; + ci->advance_buffer(consumed); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/demac/COPYING b/lib/rbcodec/codecs/demac/COPYING new file mode 100644 index 0000000000..d511905c16 --- /dev/null +++ b/lib/rbcodec/codecs/demac/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/lib/rbcodec/codecs/demac/Makefile b/lib/rbcodec/codecs/demac/Makefile new file mode 100644 index 0000000000..7843be6e49 --- /dev/null +++ b/lib/rbcodec/codecs/demac/Makefile @@ -0,0 +1,42 @@ +# $Id$ + +FILTERS = libdemac/filter_16_11.o libdemac/filter_64_11.o libdemac/filter_256_13.o libdemac/filter_32_10.o libdemac/filter_1280_15.o +LIBOBJS = libdemac/parser.o libdemac/decoder.o libdemac/entropy.o libdemac/predictor.o libdemac/crc.o $(FILTERS) +OBJS = demac.o wavwrite.o $(LIBOBJS) + +CFLAGS = -Wall -g -O3 -Ilibdemac + +ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN) +EXT = .exe +CROSS = +CFLAGS += -mno-cygwin +else + ifdef WIN + EXT = .exe + CROSS = i586-mingw32msvc- + else + EXT = + CROSS = + endif +endif + +CC = $(CROSS)gcc +STRIP = $(CROSS)strip +OUTPUT = demac$(EXT) + +all: $(OUTPUT) + +$(OUTPUT): $(OBJS) + $(CC) $(CFLAGS) -o $(OUTPUT) $(OBJS) + +.c.o : + $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +libdemac/filter_16_11.o: libdemac/filter.c +libdemac/filter_64_11.o: libdemac/filter.c +libdemac/filter_256_13.o: libdemac/filter.c +libdemac/filter_1280_15.o: libdemac/filter.c +libdemac/filter_32_10.o: libdemac/filter.c + +clean: + rm -f $(OUTPUT) $(OBJS) *~ */*~ diff --git a/lib/rbcodec/codecs/demac/README b/lib/rbcodec/codecs/demac/README new file mode 100644 index 0000000000..73a1c22485 --- /dev/null +++ b/lib/rbcodec/codecs/demac/README @@ -0,0 +1,69 @@ +demac - a decoder for Monkey's Audio files. + +Introduction + +demac is an implementation in portable ANSI C of a decoder for the +Monkey's Audio lossless compression format. It has the following +features: + + * Open source (GNU GPL) + * Written in portable ANSI C + * Designed for use on low memory and embedded devices. All internal + buffers are statically declared - the core library doesn't require + malloc/free. This has the disadvantage that the library isn't + re-entrant. + + +Compatibility + + +libdemac is still in the early stages of development but has been +relatively well tested with v3.99 files at all compression levels. + +v3.97 files have received less testing - 16-bit files seem to work, +but 24-bit files are causing problems in the range decoder. + +Files earlier than v3.97 are not supported by libdemac, but support +might be added in the future. + + +Source Code + +The source code in this directory is structured as follows: + +demac/Makefile - Makefile for the standalone demac decoder +demac/demac.c - Simple standalone test program to decoder an APE file to WAV +demac/wavwrite.[ch] - Helper functions for demac.c +demac/libdemac/Makefile - A Makefile for use in Rockbox +demac/libdemac/*.[ch] - The main libdemac code + + +Latest Version + +The latest version of demac and libdemac can always be found in the +"lib/rbcodec/codecs/demac/" directory in the Rockbox source. You can check +this out from svn with the command: + +svn co svn://svn.rockbox.org/rockbox/trunk/lib/rbcodec/codecs/demac demac + +Or browse the source code online here: + +http://svn.rockbox.org/viewvc.cgi/trunk/lib/rbcodec/codecs/demac + + + +Acknowledgements + +Thanks to Matt. T. Ashland for writing Monkey's Audio. His website +can be found here: http://www.monkeysaudio.com + + +Copyright and license + + +libdemac is (C) 2007 Dave Chapman and is licensed under the GNU +GPL. See the COPYING file in this directory. + +The exception is the majority of rangecoding.h, which is (C) 1997, +1998, 1999, 2000 Michael Schindler and is also licensed under the GPL. +See that source file for full details. diff --git a/lib/rbcodec/codecs/demac/demac.c b/lib/rbcodec/codecs/demac/demac.c new file mode 100644 index 0000000000..3e97fff2c9 --- /dev/null +++ b/lib/rbcodec/codecs/demac/demac.c @@ -0,0 +1,281 @@ +/* + +demac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +/* + +This example is intended to demonstrate how the decoder can be used in +embedded devices - there is no usage of dynamic memory (i.e. no +malloc/free) and small buffer sizes are chosen to minimise both the +memory usage and decoding latency. + +This implementation requires the following memory and supports decoding of all APE files up to 24-bit Stereo. + +32768 - data from the input stream to be presented to the decoder in one contiguous chunk. +18432 - decoding buffer (left channel) +18432 - decoding buffer (right channel) + +17408+5120+2240 - buffers used for filter histories (compression levels 2000-5000) + +In addition, this example uses a static 27648 byte buffer as temporary +storage for outputting the data to a WAV file but that could be +avoided by writing the decoded data one sample at a time. + +*/ + +#include <stdio.h> +#include <inttypes.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#include "demac.h" +#include "wavwrite.h" + +#ifndef __WIN32__ +#define O_BINARY 0 +#endif + +#define CALC_CRC 1 + +#define BLOCKS_PER_LOOP 4608 +#define MAX_CHANNELS 2 +#define MAX_BYTESPERSAMPLE 3 + +#define INPUT_CHUNKSIZE (32*1024) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +/* 4608*2*3 = 27648 bytes */ +static unsigned char wavbuffer[BLOCKS_PER_LOOP*MAX_CHANNELS*MAX_BYTESPERSAMPLE]; + +/* 4608*4 = 18432 bytes per channel */ +static int32_t decoded0[BLOCKS_PER_LOOP]; +static int32_t decoded1[BLOCKS_PER_LOOP]; + +/* We assume that 32KB of compressed data is enough to extract up to + 27648 bytes of decompressed data. */ + +static unsigned char inbuffer[INPUT_CHUNKSIZE]; + +int ape_decode(char* infile, char* outfile) +{ + int fd; + int fdwav; + int currentframe; + int nblocks; + int bytesconsumed; + struct ape_ctx_t ape_ctx; + int i, n; + unsigned char* p; + int bytesinbuffer; + int blockstodecode; + int res; + int firstbyte; + int16_t sample16; + int32_t sample32; + uint32_t frame_crc; + int crc_errors = 0; + + fd = open(infile,O_RDONLY|O_BINARY); + if (fd < 0) return -1; + + /* Read the file headers to populate the ape_ctx struct */ + if (ape_parseheader(fd,&ape_ctx) < 0) { + printf("Cannot read header\n"); + close(fd); + return -1; + } + + if ((ape_ctx.fileversion < APE_MIN_VERSION) || (ape_ctx.fileversion > APE_MAX_VERSION)) { + printf("Unsupported file version - %.2f\n", ape_ctx.fileversion/1000.0); + close(fd); + return -2; + } + + //ape_dumpinfo(&ape_ctx); + + printf("Decoding file - v%.2f, compression level %d\n",ape_ctx.fileversion/1000.0,ape_ctx.compressiontype); + + /* Open the WAV file and write a canonical 44-byte WAV header + based on the audio format information in the ape_ctx struct. + + NOTE: This example doesn't write the original WAV header and + tail data which are (optionally) stored in the APE file. + */ + fdwav = open_wav(&ape_ctx,outfile); + + currentframe = 0; + + /* Initialise the buffer */ + lseek(fd, ape_ctx.firstframe, SEEK_SET); + bytesinbuffer = read(fd, inbuffer, INPUT_CHUNKSIZE); + firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */ + + /* The main decoding loop - we decode the frames a small chunk at a time */ + while (currentframe < ape_ctx.totalframes) + { + /* Calculate how many blocks there are in this frame */ + if (currentframe == (ape_ctx.totalframes - 1)) + nblocks = ape_ctx.finalframeblocks; + else + nblocks = ape_ctx.blocksperframe; + + ape_ctx.currentframeblocks = nblocks; + + /* Initialise the frame decoder */ + init_frame_decoder(&ape_ctx, inbuffer, &firstbyte, &bytesconsumed); + + /* Update buffer */ + memmove(inbuffer,inbuffer + bytesconsumed, bytesinbuffer - bytesconsumed); + bytesinbuffer -= bytesconsumed; + + n = read(fd, inbuffer + bytesinbuffer, INPUT_CHUNKSIZE - bytesinbuffer); + bytesinbuffer += n; + +#if CALC_CRC + frame_crc = ape_initcrc(); +#endif + + /* Decode the frame a chunk at a time */ + while (nblocks > 0) + { + blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks); + + if ((res = decode_chunk(&ape_ctx, inbuffer, &firstbyte, + &bytesconsumed, + decoded0, decoded1, + blockstodecode)) < 0) + { + /* Frame decoding error, abort */ + close(fd); + return res; + } + + /* Convert the output samples to WAV format and write to output file */ + p = wavbuffer; + if (ape_ctx.bps == 8) { + for (i = 0 ; i < blockstodecode ; i++) + { + /* 8 bit WAV uses unsigned samples */ + *(p++) = (decoded0[i] + 0x80) & 0xff; + + if (ape_ctx.channels == 2) { + *(p++) = (decoded1[i] + 0x80) & 0xff; + } + } + } else if (ape_ctx.bps == 16) { + for (i = 0 ; i < blockstodecode ; i++) + { + sample16 = decoded0[i]; + *(p++) = sample16 & 0xff; + *(p++) = (sample16 >> 8) & 0xff; + + if (ape_ctx.channels == 2) { + sample16 = decoded1[i]; + *(p++) = sample16 & 0xff; + *(p++) = (sample16 >> 8) & 0xff; + } + } + } else if (ape_ctx.bps == 24) { + for (i = 0 ; i < blockstodecode ; i++) + { + sample32 = decoded0[i]; + *(p++) = sample32 & 0xff; + *(p++) = (sample32 >> 8) & 0xff; + *(p++) = (sample32 >> 16) & 0xff; + + if (ape_ctx.channels == 2) { + sample32 = decoded1[i]; + *(p++) = sample32 & 0xff; + *(p++) = (sample32 >> 8) & 0xff; + *(p++) = (sample32 >> 16) & 0xff; + } + } + } + +#if CALC_CRC + frame_crc = ape_updatecrc(wavbuffer, p - wavbuffer, frame_crc); +#endif + write(fdwav,wavbuffer,p - wavbuffer); + + /* Update the buffer */ + memmove(inbuffer,inbuffer + bytesconsumed, bytesinbuffer - bytesconsumed); + bytesinbuffer -= bytesconsumed; + + n = read(fd, inbuffer + bytesinbuffer, INPUT_CHUNKSIZE - bytesinbuffer); + bytesinbuffer += n; + + /* Decrement the block count */ + nblocks -= blockstodecode; + } + +#if CALC_CRC + frame_crc = ape_finishcrc(frame_crc); + + if (ape_ctx.CRC != frame_crc) + { + fprintf(stderr,"CRC error in frame %d\n",currentframe); + crc_errors++; + } +#endif + + currentframe++; + } + + close(fd); + close(fdwav); + + if (crc_errors > 0) + return -1; + else + return 0; +} + +int main(int argc, char* argv[]) +{ + int res; + + if (argc != 3) { + fprintf(stderr,"Usage: demac infile.ape outfile.wav\n"); + return 0; + } + + res = ape_decode(argv[1], argv[2]); + + if (res < 0) + { + fprintf(stderr,"DECODING ERROR %d, ABORTING\n", res); + } + else + { + fprintf(stderr,"DECODED OK - NO CRC ERRORS.\n"); + } + + return 0; +} diff --git a/lib/rbcodec/codecs/demac/libdemac.make b/lib/rbcodec/codecs/demac/libdemac.make new file mode 100644 index 0000000000..6f64b2ff10 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac.make @@ -0,0 +1,35 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +# libdemac +DEMACLIB := $(CODECDIR)/libdemac.a +DEMACLIB_SRC := $(call preprocess, $(RBCODECLIB_DIR)/codecs/demac/libdemac/SOURCES) +DEMACLIB_OBJ := $(call c2obj, $(DEMACLIB_SRC)) +OTHER_SRC += $(DEMACLIB_SRC) +ifeq ($(ARCH),arch_arm) +OTHER_SRC += $(RBCODECLIB_DIR)/codecs/demac/libdemac/udiv32_arm-pre.S +endif +DEMACLIB_PRE := $(subst .a,-pre.a,$(DEMACLIB)) +DEMACLIB_OBJ_PRE := $(subst udiv32_arm.o,udiv32_arm-pre.o,$(DEMACLIB_OBJ)) + +$(DEMACLIB_PRE): $(DEMACLIB_OBJ_PRE) + $(SILENT)$(shell rm -f $@) + $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null + +$(DEMACLIB): $(DEMACLIB_OBJ) + $(SILENT)$(shell rm -f $@) + $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null + +$(CODECDIR)/ape_free_iram.h: $(CODECDIR)/ape-pre.map + $(call PRINTS,GEN $(@F))perl -an \ + -e 'if(/^PLUGIN_IRAM/){$$istart=hex($$F[1]);$$ilen=hex($$F[2])}' \ + -e 'if(/iend = /){$$iend=hex($$F[0]);}' \ + -e '}{if($$ilen){print"#define FREE_IRAM ".($$ilen+$$istart-$$iend)."\n";}' \ + $(CODECDIR)/ape-pre.map \ + > $@ diff --git a/lib/rbcodec/codecs/demac/libdemac/SOURCES b/lib/rbcodec/codecs/demac/libdemac/SOURCES new file mode 100644 index 0000000000..018f35a73c --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/SOURCES @@ -0,0 +1,15 @@ +predictor.c +#ifdef CPU_ARM +predictor-arm.S +udiv32_arm.S +#elif defined CPU_COLDFIRE +predictor-cf.S +#endif +entropy.c +decoder.c +parser.c +filter_1280_15.c +filter_16_11.c +filter_256_13.c +filter_32_10.c +filter_64_11.c diff --git a/lib/rbcodec/codecs/demac/libdemac/crc.c b/lib/rbcodec/codecs/demac/libdemac/crc.c new file mode 100644 index 0000000000..fa3ea89d7e --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/crc.c @@ -0,0 +1,120 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <inttypes.h> +#include "demac.h" + +static const uint32_t crctab32[] = +{ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +uint32_t ape_initcrc(void) +{ + return 0xffffffff; +} + +/* Update the CRC from a block of WAV-format audio data */ +uint32_t ape_updatecrc(unsigned char *block, int count, uint32_t crc) +{ + while (count--) + crc = (crc >> 8) ^ crctab32[(crc & 0xff) ^ *block++]; + + return crc; +} + +uint32_t ape_finishcrc(uint32_t crc) +{ + crc ^= 0xffffffff; + crc >>= 1; + + return crc; +} + diff --git a/lib/rbcodec/codecs/demac/libdemac/decoder.c b/lib/rbcodec/codecs/demac/libdemac/decoder.c new file mode 100644 index 0000000000..b0339a75d9 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/decoder.c @@ -0,0 +1,216 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <inttypes.h> +#include <string.h> + +#include "demac.h" +#include "predictor.h" +#include "entropy.h" +#include "filter.h" +#include "demac_config.h" + +/* Statically allocate the filter buffers */ + +#ifdef FILTER256_IRAM +static filter_int filterbuf32[(32*3 + FILTER_HISTORY_SIZE) * 2] + IBSS_ATTR_DEMAC MEM_ALIGN_ATTR; + /* 2432 or 4864 bytes */ +static filter_int filterbuf256[(256*3 + FILTER_HISTORY_SIZE) * 2] + IBSS_ATTR_DEMAC MEM_ALIGN_ATTR; + /* 5120 or 10240 bytes */ +#define FILTERBUF64 filterbuf256 +#define FILTERBUF32 filterbuf32 +#define FILTERBUF16 filterbuf32 +#else +static filter_int filterbuf64[(64*3 + FILTER_HISTORY_SIZE) * 2] + IBSS_ATTR_DEMAC MEM_ALIGN_ATTR; + /* 2432 or 4864 bytes */ +static filter_int filterbuf256[(256*3 + FILTER_HISTORY_SIZE) * 2] + MEM_ALIGN_ATTR; /* 5120 or 10240 bytes */ +#define FILTERBUF64 filterbuf64 +#define FILTERBUF32 filterbuf64 +#define FILTERBUF16 filterbuf64 +#endif + +/* This is only needed for "insane" files, and no current Rockbox targets + can hope to decode them in realtime, except the Gigabeat S (at 528MHz). */ +static filter_int filterbuf1280[(1280*3 + FILTER_HISTORY_SIZE) * 2] + IBSS_ATTR_DEMAC_INSANEBUF MEM_ALIGN_ATTR; + /* 17408 or 34816 bytes */ + +void init_frame_decoder(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed) +{ + init_entropy_decoder(ape_ctx, inbuffer, firstbyte, bytesconsumed); + //printf("CRC=0x%08x\n",ape_ctx->CRC); + //printf("Flags=0x%08x\n",ape_ctx->frameflags); + + init_predictor_decoder(&ape_ctx->predictor); + + switch (ape_ctx->compressiontype) + { + case 2000: + init_filter_16_11(FILTERBUF16); + break; + + case 3000: + init_filter_64_11(FILTERBUF64); + break; + + case 4000: + init_filter_256_13(filterbuf256); + init_filter_32_10(FILTERBUF32); + break; + + case 5000: + init_filter_1280_15(filterbuf1280); + init_filter_256_13(filterbuf256); + init_filter_16_11(FILTERBUF32); + } +} + +int ICODE_ATTR_DEMAC decode_chunk(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed, + int32_t* decoded0, int32_t* decoded1, + int count) +{ + int32_t left, right; +#ifdef ROCKBOX + int scale = (APE_OUTPUT_DEPTH - ape_ctx->bps); + #define SCALE(x) ((x) << scale) +#else + #define SCALE(x) (x) +#endif + + if ((ape_ctx->channels==1) || ((ape_ctx->frameflags + & (APE_FRAMECODE_PSEUDO_STEREO|APE_FRAMECODE_STEREO_SILENCE)) + == APE_FRAMECODE_PSEUDO_STEREO)) { + + entropy_decode(ape_ctx, inbuffer, firstbyte, bytesconsumed, + decoded0, NULL, count); + + if (ape_ctx->frameflags & APE_FRAMECODE_MONO_SILENCE) { + /* We are pure silence, so we're done. */ + return 0; + } + + switch (ape_ctx->compressiontype) + { + case 2000: + apply_filter_16_11(ape_ctx->fileversion,0,decoded0,count); + break; + + case 3000: + apply_filter_64_11(ape_ctx->fileversion,0,decoded0,count); + break; + + case 4000: + apply_filter_32_10(ape_ctx->fileversion,0,decoded0,count); + apply_filter_256_13(ape_ctx->fileversion,0,decoded0,count); + break; + + case 5000: + apply_filter_16_11(ape_ctx->fileversion,0,decoded0,count); + apply_filter_256_13(ape_ctx->fileversion,0,decoded0,count); + apply_filter_1280_15(ape_ctx->fileversion,0,decoded0,count); + } + + /* Now apply the predictor decoding */ + predictor_decode_mono(&ape_ctx->predictor,decoded0,count); + + if (ape_ctx->channels==2) { + /* Pseudo-stereo - copy left channel to right channel */ + while (count--) + { + left = *decoded0; + *(decoded1++) = *(decoded0++) = SCALE(left); + } + } +#ifdef ROCKBOX + else { + /* Scale to output depth */ + while (count--) + { + left = *decoded0; + *(decoded0++) = SCALE(left); + } + } +#endif + } else { /* Stereo */ + entropy_decode(ape_ctx, inbuffer, firstbyte, bytesconsumed, + decoded0, decoded1, count); + + if ((ape_ctx->frameflags & APE_FRAMECODE_STEREO_SILENCE) + == APE_FRAMECODE_STEREO_SILENCE) { + /* We are pure silence, so we're done. */ + return 0; + } + + /* Apply filters - compression type 1000 doesn't have any */ + switch (ape_ctx->compressiontype) + { + case 2000: + apply_filter_16_11(ape_ctx->fileversion,0,decoded0,count); + apply_filter_16_11(ape_ctx->fileversion,1,decoded1,count); + break; + + case 3000: + apply_filter_64_11(ape_ctx->fileversion,0,decoded0,count); + apply_filter_64_11(ape_ctx->fileversion,1,decoded1,count); + break; + + case 4000: + apply_filter_32_10(ape_ctx->fileversion,0,decoded0,count); + apply_filter_32_10(ape_ctx->fileversion,1,decoded1,count); + apply_filter_256_13(ape_ctx->fileversion,0,decoded0,count); + apply_filter_256_13(ape_ctx->fileversion,1,decoded1,count); + break; + + case 5000: + apply_filter_16_11(ape_ctx->fileversion,0,decoded0,count); + apply_filter_16_11(ape_ctx->fileversion,1,decoded1,count); + apply_filter_256_13(ape_ctx->fileversion,0,decoded0,count); + apply_filter_256_13(ape_ctx->fileversion,1,decoded1,count); + apply_filter_1280_15(ape_ctx->fileversion,0,decoded0,count); + apply_filter_1280_15(ape_ctx->fileversion,1,decoded1,count); + } + + /* Now apply the predictor decoding */ + predictor_decode_stereo(&ape_ctx->predictor,decoded0,decoded1,count); + + /* Decorrelate and scale to output depth */ + while (count--) + { + left = *decoded1 - (*decoded0 / 2); + right = left + *decoded0; + + *(decoded0++) = SCALE(left); + *(decoded1++) = SCALE(right); + } + } + return 0; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/decoder.h b/lib/rbcodec/codecs/demac/libdemac/decoder.h new file mode 100644 index 0000000000..aeac569509 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/decoder.h @@ -0,0 +1,40 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_DECODER_H +#define _APE_DECODER_H + +#include <inttypes.h> +#include "parser.h" + +void init_frame_decoder(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed); + +int decode_chunk(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed, + int32_t* decoded0, int32_t* decoded1, + int count); +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/demac.h b/lib/rbcodec/codecs/demac/libdemac/demac.h new file mode 100644 index 0000000000..696b2aba73 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/demac.h @@ -0,0 +1,45 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_DECODER_H +#define _APE_DECODER_H + +#include <inttypes.h> +#include "parser.h" + +void init_frame_decoder(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed); + +int decode_chunk(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed, + int32_t* decoded0, int32_t* decoded1, + int count); + +uint32_t ape_initcrc(void); +uint32_t ape_updatecrc(unsigned char *block, int count, uint32_t crc); +uint32_t ape_finishcrc(uint32_t crc); + +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/demac_config.h b/lib/rbcodec/codecs/demac/libdemac/demac_config.h new file mode 100644 index 0000000000..fa4f008036 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/demac_config.h @@ -0,0 +1,145 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _DEMAC_CONFIG_H +#define _DEMAC_CONFIG_H + +/* Build-time choices for libdemac. + * Note that this file is included by both .c and .S files. */ + +#ifdef ROCKBOX + +#include "config.h" + +#ifndef __ASSEMBLER__ +#include "codeclib.h" +#include <codecs.h> +#endif + +#define APE_OUTPUT_DEPTH 29 + +/* On ARMv4, using 32 bit ints for the filters is faster. */ +#if defined(CPU_ARM) && (ARM_ARCH == 4) +#define FILTER_BITS 32 +#endif + +#if !defined(CPU_PP) && !defined(CPU_S5L870X) +#define FILTER256_IRAM +#endif + +#if CONFIG_CPU == PP5002 || defined(CPU_S5L870X) +/* Code and data IRAM for speed (PP5002 has a broken cache), not enough IRAM + * for the insane filter buffer. Reciprocal table for division in IRAM. */ +#define ICODE_SECTION_DEMAC_ARM .icode +#define ICODE_ATTR_DEMAC ICODE_ATTR +#define ICONST_ATTR_DEMAC ICONST_ATTR +#define IBSS_ATTR_DEMAC IBSS_ATTR +#define IBSS_ATTR_DEMAC_INSANEBUF + +#elif CONFIG_CPU == PP5020 +/* Code and small data in DRAM for speed (PP5020 IRAM isn't completely single + * cycle). Insane filter buffer not in IRAM in favour of reciprocal table for + * divison. Decoded data buffers should be in IRAM (defined by the caller). */ +#define ICODE_SECTION_DEMAC_ARM .text +#define ICODE_ATTR_DEMAC +#define ICONST_ATTR_DEMAC +#define IBSS_ATTR_DEMAC +#define IBSS_ATTR_DEMAC_INSANEBUF + +#elif CONFIG_CPU == PP5022 +/* Code in DRAM, data in IRAM. Insane filter buffer not in IRAM in favour of + * reciprocal table for divison */ +#define ICODE_SECTION_DEMAC_ARM .text +#define ICODE_ATTR_DEMAC +#define ICONST_ATTR_DEMAC ICONST_ATTR +#define IBSS_ATTR_DEMAC IBSS_ATTR +#define IBSS_ATTR_DEMAC_INSANEBUF + +#else +/* Code in DRAM, data in IRAM, including insane filter buffer. */ +#define ICODE_SECTION_DEMAC_ARM .text +#define ICODE_ATTR_DEMAC +#define ICONST_ATTR_DEMAC ICONST_ATTR +#define IBSS_ATTR_DEMAC IBSS_ATTR +#define IBSS_ATTR_DEMAC_INSANEBUF IBSS_ATTR +#endif + +#else /* !ROCKBOX */ + +#define APE_OUTPUT_DEPTH (ape_ctx->bps) + +#define MEM_ALIGN_ATTR __attribute__((aligned(16))) + /* adjust to target architecture for best performance */ + +#define ICODE_ATTR_DEMAC +#define ICONST_ATTR_DEMAC +#define IBSS_ATTR_DEMAC +#define IBSS_ATTR_DEMAC_INSANEBUF + +/* Use to give gcc hints on which branch is most likely taken */ +#if defined(__GNUC__) && __GNUC__ >= 3 +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif + +#endif /* !ROCKBOX */ + +/* Defaults */ + +#ifndef FILTER_HISTORY_SIZE +#define FILTER_HISTORY_SIZE 512 +#endif + +#ifndef PREDICTOR_HISTORY_SIZE +#define PREDICTOR_HISTORY_SIZE 512 +#endif + +#ifndef FILTER_BITS +#define FILTER_BITS 16 +#endif + + +#ifndef __ASSEMBLER__ + +#if defined(CPU_ARM) && (ARM_ARCH < 5 || defined(USE_IRAM)) +/* optimised unsigned integer division for ARMv4, in IRAM */ +unsigned udiv32_arm(unsigned a, unsigned b); +#define UDIV32(a, b) udiv32_arm(a, b) +#else +/* default */ +#define UDIV32(a, b) (a / b) +#endif + +#include <inttypes.h> +#if FILTER_BITS == 32 +typedef int32_t filter_int; +#elif FILTER_BITS == 16 +typedef int16_t filter_int; +#endif +#endif + +#endif /* _DEMAC_CONFIG_H */ diff --git a/lib/rbcodec/codecs/demac/libdemac/entropy.c b/lib/rbcodec/codecs/demac/libdemac/entropy.c new file mode 100644 index 0000000000..1cef979808 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/entropy.c @@ -0,0 +1,464 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <inttypes.h> +#include <string.h> + +#include "parser.h" +#include "entropy.h" +#include "demac_config.h" + +#define MODEL_ELEMENTS 64 + +/* + The following counts arrays for use with the range decoder are + hard-coded in the Monkey's Audio decoder. +*/ + +static const int counts_3970[65] ICONST_ATTR_DEMAC = +{ + 0,14824,28224,39348,47855,53994,58171,60926, + 62682,63786,64463,64878,65126,65276,65365,65419, + 65450,65469,65480,65487,65491,65493,65494,65495, + 65496,65497,65498,65499,65500,65501,65502,65503, + 65504,65505,65506,65507,65508,65509,65510,65511, + 65512,65513,65514,65515,65516,65517,65518,65519, + 65520,65521,65522,65523,65524,65525,65526,65527, + 65528,65529,65530,65531,65532,65533,65534,65535, + 65536 +}; + +/* counts_diff_3970[i] = counts_3970[i+1] - counts_3970[i] */ +static const int counts_diff_3970[64] ICONST_ATTR_DEMAC = +{ + 14824,13400,11124,8507,6139,4177,2755,1756, + 1104,677,415,248,150,89,54,31, + 19,11,7,4,2,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1 +}; + +static const int counts_3980[65] ICONST_ATTR_DEMAC = +{ + 0,19578,36160,48417,56323,60899,63265,64435, + 64971,65232,65351,65416,65447,65466,65476,65482, + 65485,65488,65490,65491,65492,65493,65494,65495, + 65496,65497,65498,65499,65500,65501,65502,65503, + 65504,65505,65506,65507,65508,65509,65510,65511, + 65512,65513,65514,65515,65516,65517,65518,65519, + 65520,65521,65522,65523,65524,65525,65526,65527, + 65528,65529,65530,65531,65532,65533,65534,65535, + 65536 +}; + +/* counts_diff_3980[i] = counts_3980[i+1] - counts_3980[i] */ + +static const int counts_diff_3980[64] ICONST_ATTR_DEMAC = +{ + 19578,16582,12257,7906,4576,2366,1170,536, + 261,119,65,31,19,10,6,3, + 3,2,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1 +}; + +/* + +Range decoder adapted from rangecod.c included in: + + http://www.compressconsult.com/rangecoder/rngcod13.zip + + rangecod.c range encoding + + (c) Michael Schindler + 1997, 1998, 1999, 2000 + http://www.compressconsult.com/ + michael@compressconsult.com + + 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. + + +The encoding functions were removed, and functions turned into "static +inline" functions. Some minor cosmetic changes were made (e.g. turning +pre-processor symbols into upper-case, removing the rc parameter from +each function (and the RNGC macro)). + +*/ + +/* BITSTREAM READING FUNCTIONS */ + +/* We deal with the input data one byte at a time - to ensure + functionality on CPUs of any endianness regardless of any requirements + for aligned reads. +*/ + +static unsigned char* bytebuffer IBSS_ATTR_DEMAC; +static int bytebufferoffset IBSS_ATTR_DEMAC; + +static inline void skip_byte(void) +{ + bytebufferoffset--; + bytebuffer += bytebufferoffset & 4; + bytebufferoffset &= 3; +} + +static inline int read_byte(void) +{ + int ch = bytebuffer[bytebufferoffset]; + + skip_byte(); + + return ch; +} + +/* RANGE DECODING FUNCTIONS */ + +/* SIZE OF RANGE ENCODING CODE VALUES. */ + +#define CODE_BITS 32 +#define TOP_VALUE ((unsigned int)1 << (CODE_BITS-1)) +#define SHIFT_BITS (CODE_BITS - 9) +#define EXTRA_BITS ((CODE_BITS-2) % 8 + 1) +#define BOTTOM_VALUE (TOP_VALUE >> 8) + +struct rangecoder_t +{ + uint32_t low; /* low end of interval */ + uint32_t range; /* length of interval */ + uint32_t help; /* bytes_to_follow resp. intermediate value */ + unsigned int buffer; /* buffer for input/output */ +}; + +static struct rangecoder_t rc IBSS_ATTR_DEMAC; + +/* Start the decoder */ +static inline void range_start_decoding(void) +{ + rc.buffer = read_byte(); + rc.low = rc.buffer >> (8 - EXTRA_BITS); + rc.range = (uint32_t) 1 << EXTRA_BITS; +} + +static inline void range_dec_normalize(void) +{ + while (rc.range <= BOTTOM_VALUE) + { + rc.buffer = (rc.buffer << 8) | read_byte(); + rc.low = (rc.low << 8) | ((rc.buffer >> 1) & 0xff); + rc.range <<= 8; + } +} + +/* Calculate culmulative frequency for next symbol. Does NO update!*/ +/* tot_f is the total frequency */ +/* or: totf is (code_value)1<<shift */ +/* returns the culmulative frequency */ +static inline int range_decode_culfreq(int tot_f) +{ + range_dec_normalize(); + rc.help = UDIV32(rc.range, tot_f); + return UDIV32(rc.low, rc.help); +} + +static inline int range_decode_culshift(int shift) +{ + range_dec_normalize(); + rc.help = rc.range >> shift; + return UDIV32(rc.low, rc.help); +} + + +/* Update decoding state */ +/* sy_f is the interval length (frequency of the symbol) */ +/* lt_f is the lower end (frequency sum of < symbols) */ +static inline void range_decode_update(int sy_f, int lt_f) +{ + rc.low -= rc.help * lt_f; + rc.range = rc.help * sy_f; +} + + +/* Decode a byte/short without modelling */ +static inline unsigned char decode_byte(void) +{ int tmp = range_decode_culshift(8); + range_decode_update( 1,tmp); + return tmp; +} + +static inline unsigned short range_decode_short(void) +{ int tmp = range_decode_culshift(16); + range_decode_update( 1,tmp); + return tmp; +} + +/* Decode n bits (n <= 16) without modelling - based on range_decode_short */ +static inline int range_decode_bits(int n) +{ int tmp = range_decode_culshift(n); + range_decode_update( 1,tmp); + return tmp; +} + + +/* Finish decoding */ +static inline void range_done_decoding(void) +{ range_dec_normalize(); /* normalize to use up all bytes */ +} + +/* + range_get_symbol_* functions based on main decoding loop in simple_d.c from + http://www.compressconsult.com/rangecoder/rngcod13.zip + (c) Michael Schindler +*/ + +static inline int range_get_symbol_3980(void) +{ + int symbol, cf; + + cf = range_decode_culshift(16); + + /* figure out the symbol inefficiently; a binary search would be much better */ + for (symbol = 0; counts_3980[symbol+1] <= cf; symbol++); + + range_decode_update(counts_diff_3980[symbol],counts_3980[symbol]); + + return symbol; +} + +static inline int range_get_symbol_3970(void) +{ + int symbol, cf; + + cf = range_decode_culshift(16); + + /* figure out the symbol inefficiently; a binary search would be much better */ + for (symbol = 0; counts_3970[symbol+1] <= cf; symbol++); + + range_decode_update(counts_diff_3970[symbol],counts_3970[symbol]); + + return symbol; +} + +/* MAIN DECODING FUNCTIONS */ + +struct rice_t +{ + uint32_t k; + uint32_t ksum; +}; + +static struct rice_t riceX IBSS_ATTR_DEMAC; +static struct rice_t riceY IBSS_ATTR_DEMAC; + +static inline void update_rice(struct rice_t* rice, int x) +{ + rice->ksum += ((x + 1) / 2) - ((rice->ksum + 16) >> 5); + + if (UNLIKELY(rice->k == 0)) { + rice->k = 1; + } else { + uint32_t lim = 1 << (rice->k + 4); + if (UNLIKELY(rice->ksum < lim)) { + rice->k--; + } else if (UNLIKELY(rice->ksum >= 2 * lim)) { + rice->k++; + } + } +} + +static inline int entropy_decode3980(struct rice_t* rice) +{ + int base, x, pivot, overflow; + + pivot = rice->ksum >> 5; + if (UNLIKELY(pivot == 0)) + pivot=1; + + overflow = range_get_symbol_3980(); + + if (UNLIKELY(overflow == (MODEL_ELEMENTS-1))) { + overflow = range_decode_short() << 16; + overflow |= range_decode_short(); + } + + if (pivot >= 0x10000) { + /* Codepath for 24-bit streams */ + int nbits, lo_bits, base_hi, base_lo; + + /* Count the number of bits in pivot */ + nbits = 17; /* We know there must be at least 17 bits */ + while ((pivot >> nbits) > 0) { nbits++; } + + /* base_lo is the low (nbits-16) bits of base + base_hi is the high 16 bits of base + */ + lo_bits = (nbits - 16); + + base_hi = range_decode_culfreq((pivot >> lo_bits) + 1); + range_decode_update(1, base_hi); + + base_lo = range_decode_culshift(lo_bits); + range_decode_update(1, base_lo); + + base = (base_hi << lo_bits) + base_lo; + } else { + /* Codepath for 16-bit streams */ + base = range_decode_culfreq(pivot); + range_decode_update(1, base); + } + + x = base + (overflow * pivot); + update_rice(rice, x); + + /* Convert to signed */ + if (x & 1) + return (x >> 1) + 1; + else + return -(x >> 1); +} + + +static inline int entropy_decode3970(struct rice_t* rice) +{ + int x, tmpk; + + int overflow = range_get_symbol_3970(); + + if (UNLIKELY(overflow == (MODEL_ELEMENTS - 1))) { + tmpk = range_decode_bits(5); + overflow = 0; + } else { + tmpk = (rice->k < 1) ? 0 : rice->k - 1; + } + + if (tmpk <= 16) { + x = range_decode_bits(tmpk); + } else { + x = range_decode_short(); + x |= (range_decode_bits(tmpk - 16) << 16); + } + x += (overflow << tmpk); + + update_rice(rice, x); + + /* Convert to signed */ + if (x & 1) + return (x >> 1) + 1; + else + return -(x >> 1); +} + +void init_entropy_decoder(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed) +{ + bytebuffer = inbuffer; + bytebufferoffset = *firstbyte; + + /* Read the CRC */ + ape_ctx->CRC = read_byte(); + ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(); + ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(); + ape_ctx->CRC = (ape_ctx->CRC << 8) | read_byte(); + + /* Read the frame flags if they exist */ + ape_ctx->frameflags = 0; + if ((ape_ctx->fileversion > 3820) && (ape_ctx->CRC & 0x80000000)) { + ape_ctx->CRC &= ~0x80000000; + + ape_ctx->frameflags = read_byte(); + ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(); + ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(); + ape_ctx->frameflags = (ape_ctx->frameflags << 8) | read_byte(); + } + /* Keep a count of the blocks decoded in this frame */ + ape_ctx->blocksdecoded = 0; + + /* Initialise the rice structs */ + riceX.k = 10; + riceX.ksum = (1 << riceX.k) * 16; + riceY.k = 10; + riceY.ksum = (1 << riceY.k) * 16; + + /* The first 8 bits of input are ignored. */ + skip_byte(); + + range_start_decoding(); + + /* Return the new state of the buffer */ + *bytesconsumed = (intptr_t)bytebuffer - (intptr_t)inbuffer; + *firstbyte = bytebufferoffset; +} + +void ICODE_ATTR_DEMAC entropy_decode(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed, + int32_t* decoded0, int32_t* decoded1, + int blockstodecode) +{ + bytebuffer = inbuffer; + bytebufferoffset = *firstbyte; + + ape_ctx->blocksdecoded += blockstodecode; + + if ((ape_ctx->frameflags & APE_FRAMECODE_LEFT_SILENCE) + && ((ape_ctx->frameflags & APE_FRAMECODE_RIGHT_SILENCE) + || (decoded1 == NULL))) { + /* We are pure silence, just memset the output buffer. */ + memset(decoded0, 0, blockstodecode * sizeof(int32_t)); + if (decoded1 != NULL) + memset(decoded1, 0, blockstodecode * sizeof(int32_t)); + } else { + if (ape_ctx->fileversion > 3970) { + while (LIKELY(blockstodecode--)) { + *(decoded0++) = entropy_decode3980(&riceY); + if (decoded1 != NULL) + *(decoded1++) = entropy_decode3980(&riceX); + } + } else { + while (LIKELY(blockstodecode--)) { + *(decoded0++) = entropy_decode3970(&riceY); + if (decoded1 != NULL) + *(decoded1++) = entropy_decode3970(&riceX); + } + } + } + + if (ape_ctx->blocksdecoded == ape_ctx->currentframeblocks) + { + range_done_decoding(); + } + + /* Return the new state of the buffer */ + *bytesconsumed = bytebuffer - inbuffer; + *firstbyte = bytebufferoffset; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/entropy.h b/lib/rbcodec/codecs/demac/libdemac/entropy.h new file mode 100644 index 0000000000..fac2a44d99 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/entropy.h @@ -0,0 +1,40 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_ENTROPY_H +#define _APE_ENTROPY_H + +#include <inttypes.h> + +void init_entropy_decoder(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed); + +void entropy_decode(struct ape_ctx_t* ape_ctx, + unsigned char* inbuffer, int* firstbyte, + int* bytesconsumed, + int32_t* decoded0, int32_t* decoded1, + int blockstodecode); + +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/filter.c b/lib/rbcodec/codecs/demac/libdemac/filter.c new file mode 100644 index 0000000000..903885cf00 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter.c @@ -0,0 +1,296 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <string.h> +#include <inttypes.h> + +#include "demac.h" +#include "filter.h" +#include "demac_config.h" + +#if FILTER_BITS == 32 + +#if defined(CPU_ARM) && (ARM_ARCH == 4) +#include "vector_math32_armv4.h" +#else +#include "vector_math_generic.h" +#endif + +#else /* FILTER_BITS == 16 */ + +#ifdef CPU_COLDFIRE +#include "vector_math16_cf.h" +#elif defined(CPU_ARM) && (ARM_ARCH >= 7) +#include "vector_math16_armv7.h" +#elif defined(CPU_ARM) && (ARM_ARCH >= 6) +#include "vector_math16_armv6.h" +#elif defined(CPU_ARM) && (ARM_ARCH >= 5) +/* Assume all our ARMv5 targets are ARMv5te(j) */ +#include "vector_math16_armv5te.h" +#elif (defined(__i386__) || defined(__i486__)) && defined(__MMX__) \ + || defined(__x86_64__) +#include "vector_math16_mmx.h" +#else +#include "vector_math_generic.h" +#endif + +#endif /* FILTER_BITS */ + +struct filter_t { + filter_int* coeffs; /* ORDER entries */ + + /* We store all the filter delays in a single buffer */ + filter_int* history_end; + + filter_int* delay; + filter_int* adaptcoeffs; + + int avg; +}; + +/* We name the functions according to the ORDER and FRACBITS + pre-processor symbols and build multiple .o files from this .c file + - this increases code-size but gives the compiler more scope for + optimising the individual functions, as well as replacing a lot of + variables with constants. +*/ + +#if FRACBITS == 11 + #if ORDER == 16 + #define INIT_FILTER init_filter_16_11 + #define APPLY_FILTER apply_filter_16_11 + #elif ORDER == 64 + #define INIT_FILTER init_filter_64_11 + #define APPLY_FILTER apply_filter_64_11 + #endif +#elif FRACBITS == 13 + #define INIT_FILTER init_filter_256_13 + #define APPLY_FILTER apply_filter_256_13 +#elif FRACBITS == 10 + #define INIT_FILTER init_filter_32_10 + #define APPLY_FILTER apply_filter_32_10 +#elif FRACBITS == 15 + #define INIT_FILTER init_filter_1280_15 + #define APPLY_FILTER apply_filter_1280_15 +#endif + +/* Some macros to handle the fixed-point stuff */ + +/* Convert from (32-FRACBITS).FRACBITS fixed-point format to an + integer (rounding to nearest). */ +#define FP_HALF (1 << (FRACBITS - 1)) /* 0.5 in fixed-point format. */ +#define FP_TO_INT(x) ((x + FP_HALF) >> FRACBITS) /* round(x) */ + +#ifdef CPU_ARM +#if ARM_ARCH >= 6 +#define SATURATE(x) ({int __res; asm("ssat %0, #16, %1" : "=r"(__res) : "r"(x)); __res; }) +#else /* ARM_ARCH < 6 */ +/* Keeping the asr #31 outside of the asm allows loads to be scheduled between + it and the rest of the block on ARM9E, with the load's result latency filled + by the other calculations. */ +#define SATURATE(x) ({ \ + int __res = (x) >> 31; \ + asm volatile ( \ + "teq %0, %1, asr #15\n\t" \ + "moveq %0, %1\n\t" \ + "eorne %0, %0, #0xff\n\t" \ + "eorne %0, %0, #0x7f00" \ + : "+r" (__res) : "r" (x) : "cc" \ + ); \ + __res; \ +}) +#endif /* ARM_ARCH */ +#else /* CPU_ARM */ +#define SATURATE(x) (LIKELY((x) == (int16_t)(x)) ? (x) : ((x) >> 31) ^ 0x7FFF) +#endif + +/* Apply the filter with state f to count entries in data[] */ + +static void ICODE_ATTR_DEMAC do_apply_filter_3980(struct filter_t* f, + int32_t* data, int count) +{ + int res; + int absres; + +#ifdef PREPARE_SCALARPRODUCT + PREPARE_SCALARPRODUCT +#endif + + while(LIKELY(count--)) + { +#ifdef FUSED_VECTOR_MATH + if (LIKELY(*data != 0)) { + if (*data < 0) + res = vector_sp_add(f->coeffs, f->delay - ORDER, + f->adaptcoeffs - ORDER); + else + res = vector_sp_sub(f->coeffs, f->delay - ORDER, + f->adaptcoeffs - ORDER); + } else { + res = scalarproduct(f->coeffs, f->delay - ORDER); + } + res = FP_TO_INT(res); +#else + res = FP_TO_INT(scalarproduct(f->coeffs, f->delay - ORDER)); + + if (LIKELY(*data != 0)) { + if (*data < 0) + vector_add(f->coeffs, f->adaptcoeffs - ORDER); + else + vector_sub(f->coeffs, f->adaptcoeffs - ORDER); + } +#endif + + res += *data; + + *data++ = res; + + /* Update the output history */ + *f->delay++ = SATURATE(res); + + /* Version 3.98 and later files */ + + /* Update the adaption coefficients */ + absres = (res < 0 ? -res : res); + + if (UNLIKELY(absres > 3 * f->avg)) + *f->adaptcoeffs = ((res >> 25) & 64) - 32; + else if (3 * absres > 4 * f->avg) + *f->adaptcoeffs = ((res >> 26) & 32) - 16; + else if (LIKELY(absres > 0)) + *f->adaptcoeffs = ((res >> 27) & 16) - 8; + else + *f->adaptcoeffs = 0; + + f->avg += (absres - f->avg) / 16; + + f->adaptcoeffs[-1] >>= 1; + f->adaptcoeffs[-2] >>= 1; + f->adaptcoeffs[-8] >>= 1; + + f->adaptcoeffs++; + + /* Have we filled the history buffer? */ + if (UNLIKELY(f->delay == f->history_end)) { + memmove(f->coeffs + ORDER, f->delay - (ORDER*2), + (ORDER*2) * sizeof(filter_int)); + f->adaptcoeffs = f->coeffs + ORDER*2; + f->delay = f->coeffs + ORDER*3; + } + } +} + +static void ICODE_ATTR_DEMAC do_apply_filter_3970(struct filter_t* f, + int32_t* data, int count) +{ + int res; + +#ifdef PREPARE_SCALARPRODUCT + PREPARE_SCALARPRODUCT +#endif + + while(LIKELY(count--)) + { +#ifdef FUSED_VECTOR_MATH + if (LIKELY(*data != 0)) { + if (*data < 0) + res = vector_sp_add(f->coeffs, f->delay - ORDER, + f->adaptcoeffs - ORDER); + else + res = vector_sp_sub(f->coeffs, f->delay - ORDER, + f->adaptcoeffs - ORDER); + } else { + res = scalarproduct(f->coeffs, f->delay - ORDER); + } + res = FP_TO_INT(res); +#else + res = FP_TO_INT(scalarproduct(f->coeffs, f->delay - ORDER)); + + if (LIKELY(*data != 0)) { + if (*data < 0) + vector_add(f->coeffs, f->adaptcoeffs - ORDER); + else + vector_sub(f->coeffs, f->adaptcoeffs - ORDER); + } +#endif + + /* Convert res from (32-FRACBITS).FRACBITS fixed-point format to an + integer (rounding to nearest) and add the input value to + it */ + res += *data; + + *data++ = res; + + /* Update the output history */ + *f->delay++ = SATURATE(res); + + /* Version ??? to < 3.98 files (untested) */ + f->adaptcoeffs[0] = (res == 0) ? 0 : ((res >> 28) & 8) - 4; + f->adaptcoeffs[-4] >>= 1; + f->adaptcoeffs[-8] >>= 1; + + f->adaptcoeffs++; + + /* Have we filled the history buffer? */ + if (UNLIKELY(f->delay == f->history_end)) { + memmove(f->coeffs + ORDER, f->delay - (ORDER*2), + (ORDER*2) * sizeof(filter_int)); + f->adaptcoeffs = f->coeffs + ORDER*2; + f->delay = f->coeffs + ORDER*3; + } + } +} + +static struct filter_t filter[2] IBSS_ATTR_DEMAC; + +static void do_init_filter(struct filter_t* f, filter_int* buf) +{ + f->coeffs = buf; + f->history_end = buf + ORDER*3 + FILTER_HISTORY_SIZE; + + /* Init pointers */ + f->adaptcoeffs = f->coeffs + ORDER*2; + f->delay = f->coeffs + ORDER*3; + + /* Zero coefficients and history buffer */ + memset(f->coeffs, 0, ORDER*3 * sizeof(filter_int)); + + /* Zero the running average */ + f->avg = 0; +} + +void INIT_FILTER(filter_int* buf) +{ + do_init_filter(&filter[0], buf); + do_init_filter(&filter[1], buf + ORDER*3 + FILTER_HISTORY_SIZE); +} + +void ICODE_ATTR_DEMAC APPLY_FILTER(int fileversion, int channel, + int32_t* data, int count) +{ + if (fileversion >= 3980) + do_apply_filter_3980(&filter[channel], data, count); + else + do_apply_filter_3970(&filter[channel], data, count); +} diff --git a/lib/rbcodec/codecs/demac/libdemac/filter.h b/lib/rbcodec/codecs/demac/libdemac/filter.h new file mode 100644 index 0000000000..609ea12496 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter.h @@ -0,0 +1,50 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_FILTER_H +#define _APE_FILTER_H + +#include "demac_config.h" + +void init_filter_16_11(filter_int* buf); +void apply_filter_16_11(int fileversion, int channel, + int32_t* decoded, int count); + +void init_filter_64_11(filter_int* buf); +void apply_filter_64_11(int fileversion, int channel, + int32_t* decoded, int count); + +void init_filter_32_10(filter_int* buf); +void apply_filter_32_10(int fileversion, int channel, + int32_t* decoded, int count); + +void init_filter_256_13(filter_int* buf); +void apply_filter_256_13(int fileversion, int channel, + int32_t* decoded, int count); + +void init_filter_1280_15(filter_int* buf); +void apply_filter_1280_15(int fileversion, int channel, + int32_t* decoded, int count); + +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/filter_1280_15.c b/lib/rbcodec/codecs/demac/libdemac/filter_1280_15.c new file mode 100644 index 0000000000..f2301fb02a --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter_1280_15.c @@ -0,0 +1,32 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include "demac_config.h" +#ifndef FILTER256_IRAM +#undef ICODE_ATTR_DEMAC +#define ICODE_ATTR_DEMAC +#endif +#define ORDER 1280 +#define FRACBITS 15 +#include "filter.c" diff --git a/lib/rbcodec/codecs/demac/libdemac/filter_16_11.c b/lib/rbcodec/codecs/demac/libdemac/filter_16_11.c new file mode 100644 index 0000000000..94c56e247f --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter_16_11.c @@ -0,0 +1,27 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define ORDER 16 +#define FRACBITS 11 +#include "filter.c" diff --git a/lib/rbcodec/codecs/demac/libdemac/filter_256_13.c b/lib/rbcodec/codecs/demac/libdemac/filter_256_13.c new file mode 100644 index 0000000000..9e4b9fcb13 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter_256_13.c @@ -0,0 +1,32 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include "demac_config.h" +#ifndef FILTER256_IRAM +#undef ICODE_ATTR_DEMAC +#define ICODE_ATTR_DEMAC +#endif +#define ORDER 256 +#define FRACBITS 13 +#include "filter.c" diff --git a/lib/rbcodec/codecs/demac/libdemac/filter_32_10.c b/lib/rbcodec/codecs/demac/libdemac/filter_32_10.c new file mode 100644 index 0000000000..5ec85089db --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter_32_10.c @@ -0,0 +1,27 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define ORDER 32 +#define FRACBITS 10 +#include "filter.c" diff --git a/lib/rbcodec/codecs/demac/libdemac/filter_64_11.c b/lib/rbcodec/codecs/demac/libdemac/filter_64_11.c new file mode 100644 index 0000000000..cd74fa5f6b --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/filter_64_11.c @@ -0,0 +1,27 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define ORDER 64 +#define FRACBITS 11 +#include "filter.c" diff --git a/lib/rbcodec/codecs/demac/libdemac/parser.c b/lib/rbcodec/codecs/demac/libdemac/parser.c new file mode 100644 index 0000000000..2af4a292b8 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/parser.c @@ -0,0 +1,402 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <inttypes.h> +#include <string.h> +#ifndef ROCKBOX +#include <stdio.h> +#include <stdlib.h> +#include "inttypes.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#endif + +#include "parser.h" + +#ifdef APE_MAX +#undef APE_MAX +#endif +#define APE_MAX(a,b) ((a)>(b)?(a):(b)) + + +static inline int16_t get_int16(unsigned char* buf) +{ + return(buf[0] | (buf[1] << 8)); +} + +static inline uint16_t get_uint16(unsigned char* buf) +{ + return(buf[0] | (buf[1] << 8)); +} + +static inline uint32_t get_uint32(unsigned char* buf) +{ + return(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); +} + + +int ape_parseheaderbuf(unsigned char* buf, struct ape_ctx_t* ape_ctx) +{ + unsigned char* header; + + memset(ape_ctx,0,sizeof(struct ape_ctx_t)); + /* TODO: Skip any leading junk such as id3v2 tags */ + ape_ctx->junklength = 0; + + memcpy(ape_ctx->magic, buf, 4); + if (memcmp(ape_ctx->magic,"MAC ",4)!=0) + { + return -1; + } + + ape_ctx->fileversion = get_int16(buf + 4); + + if (ape_ctx->fileversion >= 3980) + { + ape_ctx->padding1 = get_int16(buf + 6); + ape_ctx->descriptorlength = get_uint32(buf + 8); + ape_ctx->headerlength = get_uint32(buf + 12); + ape_ctx->seektablelength = get_uint32(buf + 16); + ape_ctx->wavheaderlength = get_uint32(buf + 20); + ape_ctx->audiodatalength = get_uint32(buf + 24); + ape_ctx->audiodatalength_high = get_uint32(buf + 28); + ape_ctx->wavtaillength = get_uint32(buf + 32); + memcpy(ape_ctx->md5, buf + 36, 16); + + header = buf + ape_ctx->descriptorlength; + + /* Read header data */ + ape_ctx->compressiontype = get_uint16(header + 0); + ape_ctx->formatflags = get_uint16(header + 2); + ape_ctx->blocksperframe = get_uint32(header + 4); + ape_ctx->finalframeblocks = get_uint32(header + 8); + ape_ctx->totalframes = get_uint32(header + 12); + ape_ctx->bps = get_uint16(header + 16); + ape_ctx->channels = get_uint16(header + 18); + ape_ctx->samplerate = get_uint32(header + 20); + + ape_ctx->seektablefilepos = ape_ctx->junklength + + ape_ctx->descriptorlength + + ape_ctx->headerlength; + + ape_ctx->firstframe = ape_ctx->junklength + ape_ctx->descriptorlength + + ape_ctx->headerlength + ape_ctx->seektablelength + + ape_ctx->wavheaderlength; + } else { + ape_ctx->headerlength = 32; + ape_ctx->compressiontype = get_uint16(buf + 6); + ape_ctx->formatflags = get_uint16(buf + 8); + ape_ctx->channels = get_uint16(buf + 10); + ape_ctx->samplerate = get_uint32(buf + 12); + ape_ctx->wavheaderlength = get_uint32(buf + 16); + ape_ctx->totalframes = get_uint32(buf + 24); + ape_ctx->finalframeblocks = get_uint32(buf + 28); + + if (ape_ctx->formatflags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL) + { + ape_ctx->headerlength += 4; + } + + if (ape_ctx->formatflags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) + { + ape_ctx->seektablelength = get_uint32(buf + ape_ctx->headerlength); + ape_ctx->seektablelength *= sizeof(int32_t); + ape_ctx->headerlength += 4; + } else { + ape_ctx->seektablelength = ape_ctx->totalframes * sizeof(int32_t); + } + + if (ape_ctx->formatflags & MAC_FORMAT_FLAG_8_BIT) + ape_ctx->bps = 8; + else if (ape_ctx->formatflags & MAC_FORMAT_FLAG_24_BIT) + ape_ctx->bps = 24; + else + ape_ctx->bps = 16; + + if (ape_ctx->fileversion >= 3950) + ape_ctx->blocksperframe = 73728 * 4; + else if ((ape_ctx->fileversion >= 3900) || (ape_ctx->fileversion >= 3800 && ape_ctx->compressiontype >= 4000)) + ape_ctx->blocksperframe = 73728; + else + ape_ctx->blocksperframe = 9216; + + ape_ctx->seektablefilepos = ape_ctx->junklength + ape_ctx->headerlength + + ape_ctx->wavheaderlength; + + ape_ctx->firstframe = ape_ctx->junklength + ape_ctx->headerlength + + ape_ctx->wavheaderlength + ape_ctx->seektablelength; + } + + ape_ctx->totalsamples = ape_ctx->finalframeblocks; + if (ape_ctx->totalframes > 1) + ape_ctx->totalsamples += ape_ctx->blocksperframe * (ape_ctx->totalframes-1); + + ape_ctx->numseekpoints = APE_MAX(ape_ctx->maxseekpoints, + ape_ctx->seektablelength / sizeof(int32_t)); + + return 0; +} + + +#ifndef ROCKBOX +/* Helper functions */ + +static int read_uint16(int fd, uint16_t* x) +{ + unsigned char tmp[2]; + int n; + + n = read(fd,tmp,2); + + if (n != 2) + return -1; + + *x = tmp[0] | (tmp[1] << 8); + + return 0; +} + +static int read_int16(int fd, int16_t* x) +{ + return read_uint16(fd, (uint16_t*)x); +} + +static int read_uint32(int fd, uint32_t* x) +{ + unsigned char tmp[4]; + int n; + + n = read(fd,tmp,4); + + if (n != 4) + return -1; + + *x = tmp[0] | (tmp[1] << 8) | (tmp[2] << 16) | (tmp[3] << 24); + + return 0; +} + +int ape_parseheader(int fd, struct ape_ctx_t* ape_ctx) +{ + int i,n; + + /* TODO: Skip any leading junk such as id3v2 tags */ + ape_ctx->junklength = 0; + + lseek(fd,ape_ctx->junklength,SEEK_SET); + + n = read(fd,&ape_ctx->magic,4); + if (n != 4) return -1; + + if (memcmp(ape_ctx->magic,"MAC ",4)!=0) + { + return -1; + } + + if (read_int16(fd,&ape_ctx->fileversion) < 0) + return -1; + + if (ape_ctx->fileversion >= 3980) + { + if (read_int16(fd,&ape_ctx->padding1) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->descriptorlength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->headerlength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->seektablelength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->wavheaderlength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->audiodatalength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->audiodatalength_high) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->wavtaillength) < 0) + return -1; + if (read(fd,&ape_ctx->md5,16) != 16) + return -1; + + /* Skip any unknown bytes at the end of the descriptor. This is for future + compatibility */ + if (ape_ctx->descriptorlength > 52) + lseek(fd,ape_ctx->descriptorlength - 52, SEEK_CUR); + + /* Read header data */ + if (read_uint16(fd,&ape_ctx->compressiontype) < 0) + return -1; + if (read_uint16(fd,&ape_ctx->formatflags) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->blocksperframe) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->finalframeblocks) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->totalframes) < 0) + return -1; + if (read_uint16(fd,&ape_ctx->bps) < 0) + return -1; + if (read_uint16(fd,&ape_ctx->channels) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->samplerate) < 0) + return -1; + } else { + ape_ctx->descriptorlength = 0; + ape_ctx->headerlength = 32; + + if (read_uint16(fd,&ape_ctx->compressiontype) < 0) + return -1; + if (read_uint16(fd,&ape_ctx->formatflags) < 0) + return -1; + if (read_uint16(fd,&ape_ctx->channels) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->samplerate) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->wavheaderlength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->wavtaillength) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->totalframes) < 0) + return -1; + if (read_uint32(fd,&ape_ctx->finalframeblocks) < 0) + return -1; + + if (ape_ctx->formatflags & MAC_FORMAT_FLAG_HAS_PEAK_LEVEL) + { + lseek(fd, 4, SEEK_CUR); /* Skip the peak level */ + ape_ctx->headerlength += 4; + } + + if (ape_ctx->formatflags & MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS) + { + if (read_uint32(fd,&ape_ctx->seektablelength) < 0) + return -1; + ape_ctx->headerlength += 4; + ape_ctx->seektablelength *= sizeof(int32_t); + } else { + ape_ctx->seektablelength = ape_ctx->totalframes * sizeof(int32_t); + } + + if (ape_ctx->formatflags & MAC_FORMAT_FLAG_8_BIT) + ape_ctx->bps = 8; + else if (ape_ctx->formatflags & MAC_FORMAT_FLAG_24_BIT) + ape_ctx->bps = 24; + else + ape_ctx->bps = 16; + + if (ape_ctx->fileversion >= 3950) + ape_ctx->blocksperframe = 73728 * 4; + else if ((ape_ctx->fileversion >= 3900) || (ape_ctx->fileversion >= 3800 && ape_ctx->compressiontype >= 4000)) + ape_ctx->blocksperframe = 73728; + else + ape_ctx->blocksperframe = 9216; + + /* Skip any stored wav header */ + if (!(ape_ctx->formatflags & MAC_FORMAT_FLAG_CREATE_WAV_HEADER)) + { + lseek(fd, ape_ctx->wavheaderlength, SEEK_CUR); + } + } + + ape_ctx->totalsamples = ape_ctx->finalframeblocks; + if (ape_ctx->totalframes > 1) + ape_ctx->totalsamples += ape_ctx->blocksperframe * (ape_ctx->totalframes-1); + + if (ape_ctx->seektablelength > 0) + { + ape_ctx->seektable = malloc(ape_ctx->seektablelength); + if (ape_ctx->seektable == NULL) + return -1; + for (i=0; i < ape_ctx->seektablelength / sizeof(uint32_t); i++) + { + if (read_uint32(fd,&ape_ctx->seektable[i]) < 0) + { + free(ape_ctx->seektable); + return -1; + } + } + } + + ape_ctx->firstframe = ape_ctx->junklength + ape_ctx->descriptorlength + + ape_ctx->headerlength + ape_ctx->seektablelength + + ape_ctx->wavheaderlength; + + return 0; +} + +void ape_dumpinfo(struct ape_ctx_t* ape_ctx) +{ + int i; + + printf("Descriptor Block:\n\n"); + printf("magic = \"%c%c%c%c\"\n", + ape_ctx->magic[0],ape_ctx->magic[1], + ape_ctx->magic[2],ape_ctx->magic[3]); + printf("fileversion = %d\n",ape_ctx->fileversion); + printf("descriptorlength = %d\n",ape_ctx->descriptorlength); + printf("headerlength = %d\n",ape_ctx->headerlength); + printf("seektablelength = %d\n",ape_ctx->seektablelength); + printf("wavheaderlength = %d\n",ape_ctx->wavheaderlength); + printf("audiodatalength = %d\n",ape_ctx->audiodatalength); + printf("audiodatalength_high = %d\n",ape_ctx->audiodatalength_high); + printf("wavtaillength = %d\n",ape_ctx->wavtaillength); + printf("md5 = "); + for (i = 0; i < 16; i++) + printf("%02x",ape_ctx->md5[i]); + printf("\n"); + + printf("\nHeader Block:\n\n"); + + printf("compressiontype = %d\n",ape_ctx->compressiontype); + printf("formatflags = %d\n",ape_ctx->formatflags); + printf("blocksperframe = %d\n",ape_ctx->blocksperframe); + printf("finalframeblocks = %d\n",ape_ctx->finalframeblocks); + printf("totalframes = %d\n",ape_ctx->totalframes); + printf("bps = %d\n",ape_ctx->bps); + printf("channels = %d\n",ape_ctx->channels); + printf("samplerate = %d\n",ape_ctx->samplerate); + + printf("\nSeektable\n\n"); + if ((ape_ctx->seektablelength / sizeof(uint32_t)) != ape_ctx->totalframes) + { + printf("No seektable\n"); + } + else + { + for ( i = 0; i < ape_ctx->seektablelength / sizeof(uint32_t) ; i++) + { + if (i < ape_ctx->totalframes-1) { + printf("%8d %d (%d bytes)\n",i,ape_ctx->seektable[i],ape_ctx->seektable[i+1]-ape_ctx->seektable[i]); + } else { + printf("%8d %d\n",i,ape_ctx->seektable[i]); + } + } + } + printf("\nCalculated information:\n\n"); + printf("junklength = %d\n",ape_ctx->junklength); + printf("firstframe = %d\n",ape_ctx->firstframe); + printf("totalsamples = %d\n",ape_ctx->totalsamples); +} + +#endif /* !ROCKBOX */ diff --git a/lib/rbcodec/codecs/demac/libdemac/parser.h b/lib/rbcodec/codecs/demac/libdemac/parser.h new file mode 100644 index 0000000000..6f07deac12 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/parser.h @@ -0,0 +1,137 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_PARSER_H +#define _APE_PARSER_H + +#include <inttypes.h> +#include "demac_config.h" + +/* The earliest and latest file formats supported by this library */ +#define APE_MIN_VERSION 3970 +#define APE_MAX_VERSION 3990 + +#define MAC_FORMAT_FLAG_8_BIT 1 // is 8-bit [OBSOLETE] +#define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE] +#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE] +#define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE] +#define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS 16 // has the number of seek elements after the peak level +#define MAC_FORMAT_FLAG_CREATE_WAV_HEADER 32 // create the wave header on decompression (not stored) + + +/* Special frame codes: + + MONO_SILENCE - All PCM samples in frame are zero (mono streams only) + LEFT_SILENCE - All PCM samples for left channel in frame are zero (stereo streams) + RIGHT_SILENCE - All PCM samples for left channel in frame are zero (stereo streams) + PSEUDO_STEREO - Left and Right channels are identical + +*/ + +#define APE_FRAMECODE_MONO_SILENCE 1 +#define APE_FRAMECODE_LEFT_SILENCE 1 /* same as mono */ +#define APE_FRAMECODE_RIGHT_SILENCE 2 +#define APE_FRAMECODE_STEREO_SILENCE 3 /* combined */ +#define APE_FRAMECODE_PSEUDO_STEREO 4 + +#define PREDICTOR_ORDER 8 +/* Total size of all predictor histories - 50 * sizeof(int32_t) */ +#define PREDICTOR_SIZE 50 + + +/* NOTE: This struct is used in predictor-arm.S - any updates need to + be reflected there. */ + +struct predictor_t +{ + /* Filter histories */ + int32_t* buf; + + int32_t YlastA; + int32_t XlastA; + + /* NOTE: The order of the next four fields is important for + predictor-arm.S */ + int32_t YfilterB; + int32_t XfilterA; + int32_t XfilterB; + int32_t YfilterA; + + /* Adaption co-efficients */ + int32_t YcoeffsA[4]; + int32_t XcoeffsA[4]; + int32_t YcoeffsB[5]; + int32_t XcoeffsB[5]; + int32_t historybuffer[PREDICTOR_HISTORY_SIZE + PREDICTOR_SIZE]; +}; + +struct ape_ctx_t +{ + /* Derived fields */ + uint32_t junklength; + uint32_t firstframe; + uint32_t totalsamples; + + /* Info from Descriptor Block */ + char magic[4]; + int16_t fileversion; + int16_t padding1; + uint32_t descriptorlength; + uint32_t headerlength; + uint32_t seektablelength; + uint32_t wavheaderlength; + uint32_t audiodatalength; + uint32_t audiodatalength_high; + uint32_t wavtaillength; + uint8_t md5[16]; + + /* Info from Header Block */ + uint16_t compressiontype; + uint16_t formatflags; + uint32_t blocksperframe; + uint32_t finalframeblocks; + uint32_t totalframes; + uint16_t bps; + uint16_t channels; + uint32_t samplerate; + + /* Seektable */ + uint32_t* seektable; /* Seektable buffer */ + uint32_t maxseekpoints; /* Max seekpoints we can store (size of seektable buffer) */ + uint32_t numseekpoints; /* Number of seekpoints */ + int seektablefilepos; /* Location in .ape file of seektable */ + + /* Decoder state */ + uint32_t CRC; + int frameflags; + int currentframeblocks; + int blocksdecoded; + struct predictor_t predictor; +}; + +int ape_parseheader(int fd, struct ape_ctx_t* ape_ctx); +int ape_parseheaderbuf(unsigned char* buf, struct ape_ctx_t* ape_ctx); +void ape_dumpinfo(struct ape_ctx_t* ape_ctx); + +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/predictor-arm.S b/lib/rbcodec/codecs/demac/libdemac/predictor-arm.S new file mode 100644 index 0000000000..92a78ed9b4 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/predictor-arm.S @@ -0,0 +1,702 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ +#include "demac_config.h" + + .section ICODE_SECTION_DEMAC_ARM,"ax",%progbits + + .align 2 + +/* NOTE: The following need to be kept in sync with parser.h */ + +#define YDELAYA 200 +#define YDELAYB 168 +#define XDELAYA 136 +#define XDELAYB 104 +#define YADAPTCOEFFSA 72 +#define XADAPTCOEFFSA 56 +#define YADAPTCOEFFSB 40 +#define XADAPTCOEFFSB 20 + +/* struct predictor_t members: */ +#define buf 0 /* int32_t* buf */ + +#define YlastA 4 /* int32_t YlastA; */ +#define XlastA 8 /* int32_t XlastA; */ + +#define YfilterB 12 /* int32_t YfilterB; */ +#define XfilterA 16 /* int32_t XfilterA; */ + +#define XfilterB 20 /* int32_t XfilterB; */ +#define YfilterA 24 /* int32_t YfilterA; */ + +#define YcoeffsA 28 /* int32_t YcoeffsA[4]; */ +#define XcoeffsA 44 /* int32_t XcoeffsA[4]; */ +#define YcoeffsB 60 /* int32_t YcoeffsB[5]; */ +#define XcoeffsB 80 /* int32_t XcoeffsB[5]; */ + +#define historybuffer 100 /* int32_t historybuffer[] */ + +@ Macro for loading 2 registers, for various ARM versions. +@ Registers must start with an even register, and must be consecutive. + +.macro LDR2OFS reg1, reg2, base, offset +#if ARM_ARCH >= 6 + ldrd \reg1, [\base, \offset] +#else /* ARM_ARCH < 6 */ +#ifdef CPU_ARM7TDMI + add \reg1, \base, \offset + ldmia \reg1, {\reg1, \reg2} +#else /* ARM9 (v4 and v5) is faster this way */ + ldr \reg1, [\base, \offset] + ldr \reg2, [\base, \offset+4] +#endif +#endif /* ARM_ARCH */ +.endm + +@ Macro for storing 2 registers, for various ARM versions. +@ Registers must start with an even register, and must be consecutive. + +.macro STR2OFS reg1, reg2, base, offset +#if ARM_ARCH >= 6 + strd \reg1, [\base, \offset] +#else + str \reg1, [\base, \offset] + str \reg2, [\base, \offset+4] +#endif +.endm + + .global predictor_decode_stereo + .type predictor_decode_stereo,%function + +@ Register usage: +@ +@ r0-r11 - scratch +@ r12 - struct predictor_t* p +@ r14 - int32_t* p->buf + +@ void predictor_decode_stereo(struct predictor_t* p, +@ int32_t* decoded0, +@ int32_t* decoded1, +@ int count) + +predictor_decode_stereo: + stmdb sp!, {r1-r11, lr} + + @ r1 (decoded0) is [sp] + @ r2 (decoded1) is [sp, #4] + @ r3 (count) is [sp, #8] + + mov r12, r0 @ r12 := p + ldr r14, [r0] @ r14 := p->buf + +loop: + +@@@@@@@@@@@@@@@@@@@@@@@@@@@ PREDICTOR Y + +@ Predictor Y, Filter A + + ldr r11, [r12, #YlastA] @ r11 := p->YlastA + + add r2, r14, #YDELAYA-12 @ r2 := &p->buf[YDELAYA-3] + ldmia r2, {r2, r3, r10} @ r2 := p->buf[YDELAYA-3] + @ r3 := p->buf[YDELAYA-2] + @ r10 := p->buf[YDELAYA-1] + + add r6, r12, #YcoeffsA + ldmia r6, {r6 - r9} @ r6 := p->YcoeffsA[0] + @ r7 := p->YcoeffsA[1] + @ r8 := p->YcoeffsA[2] + @ r9 := p->YcoeffsA[3] + + subs r10, r11, r10 @ r10 := r11 - r10 + + STR2OFS r10, r11, r14, #YDELAYA-4 + @ p->buf[YDELAYA-1] = r10 + @ p->buf[YDELAYA] = r11 + + mul r0, r11, r6 @ r0 := p->buf[YDELAYA] * p->YcoeffsA[0] + mla r0, r10, r7, r0 @ r0 += p->buf[YDELAYA-1] * p->YcoeffsA[1] + mla r0, r3, r8, r0 @ r0 += p->buf[YDELAYA-2] * p->YcoeffsA[2] + mla r0, r2, r9, r0 @ r0 += p->buf[YDELAYA-3] * p->YcoeffsA[3] + + @ flags were set above, in the subs instruction + mvngt r10, #0 + movlt r10, #1 @ r10 := SIGN(r10) (see .c for SIGN macro) + + cmp r11, #0 + mvngt r11, #0 + movlt r11, #1 @ r11 := SIGN(r11) (see .c for SIGN macro) + + STR2OFS r10, r11, r14, #YADAPTCOEFFSA-4 + @ p->buf[YADAPTCOEFFSA-1] := r10 + @ p->buf[YADAPTCOEFFSA] := r11 + + @ NOTE: r0 now contains predictionA - don't overwrite. + +@ Predictor Y, Filter B + + LDR2OFS r6, r7, r12, #YfilterB @ r6 := p->YfilterB + @ r7 := p->XfilterA + + add r2, r14, #YDELAYB-16 @ r2 := &p->buf[YDELAYB-4] + ldmia r2, {r2 - r4, r10} @ r2 := p->buf[YDELAYB-4] + @ r3 := p->buf[YDELAYB-3] + @ r4 := p->buf[YDELAYB-2] + @ r10 := p->buf[YDELAYB-1] + + rsb r6, r6, r6, lsl #5 @ r6 := r6 * 32 - r6 ( == r6*31) + sub r11, r7, r6, asr #5 @ r11 (p->buf[YDELAYB]) := r7 - (r6 >> 5) + + str r7, [r12, #YfilterB] @ p->YfilterB := r7 (p->XfilterA) + + add r5, r12, #YcoeffsB + ldmia r5, {r5 - r9} @ r5 := p->YcoeffsB[0] + @ r6 := p->YcoeffsB[1] + @ r7 := p->YcoeffsB[2] + @ r8 := p->YcoeffsB[3] + @ r9 := p->YcoeffsB[4] + + subs r10, r11, r10 @ r10 := r11 - r10 + + STR2OFS r10, r11, r14, #YDELAYB-4 + @ p->buf[YDELAYB-1] = r10 + @ p->buf[YDELAYB] = r11 + + mul r1, r11, r5 @ r1 := p->buf[YDELAYB] * p->YcoeffsB[0] + mla r1, r10, r6, r1 @ r1 += p->buf[YDELAYB-1] * p->YcoeffsB[1] + mla r1, r4, r7, r1 @ r1 += p->buf[YDELAYB-2] * p->YcoeffsB[2] + mla r1, r3, r8, r1 @ r1 += p->buf[YDELAYB-3] * p->YcoeffsB[3] + mla r1, r2, r9, r1 @ r1 += p->buf[YDELAYB-4] * p->YcoeffsB[4] + + @ flags were set above, in the subs instruction + mvngt r10, #0 + movlt r10, #1 @ r10 := SIGN(r10) (see .c for SIGN macro) + + cmp r11, #0 + mvngt r11, #0 + movlt r11, #1 @ r11 := SIGN(r11) (see .c for SIGN macro) + + STR2OFS r10, r11, r14, #YADAPTCOEFFSB-4 + @ p->buf[YADAPTCOEFFSB-1] := r10 + @ p->buf[YADAPTCOEFFSB] := r11 + + @ r0 still contains predictionA + @ r1 contains predictionB + + @ Finish Predictor Y + + ldr r2, [sp] @ r2 := decoded0 + add r0, r0, r1, asr #1 @ r0 := r0 + (r1 >> 1) + ldr r4, [r12, #YfilterA] @ r4 := p->YfilterA + ldr r3, [r2] @ r3 := *decoded0 + rsb r4, r4, r4, lsl #5 @ r4 := r4 * 32 - r4 ( == r4*31) + add r1, r3, r0, asr #10 @ r1 := r3 + (r0 >> 10) + str r1, [r12, #YlastA] @ p->YlastA := r1 + add r1, r1, r4, asr #5 @ r1 := r1 + (r4 >> 5) + str r1, [r12, #YfilterA] @ p->YfilterA := r1 + + @ r1 contains p->YfilterA + @ r2 contains decoded0 + @ r3 contains *decoded0 + + @ r5, r6, r7, r8, r9 contain p->YcoeffsB[0..4] + @ r10, r11 contain p->buf[YADAPTCOEFFSB-1] and p->buf[YADAPTCOEFFSB] + + str r1, [r2], #4 @ *(decoded0++) := r1 (p->YfilterA) + str r2, [sp] @ save decoded0 + cmp r3, #0 + beq 3f + + add r2, r14, #YADAPTCOEFFSB-16 + ldmia r2, {r2 - r4} @ r2 := p->buf[YADAPTCOEFFSB-4] + @ r3 := p->buf[YADAPTCOEFFSB-3] + @ r4 := p->buf[YADAPTCOEFFSB-2] + blt 1f + + @ *decoded0 > 0 + + sub r5, r5, r11 @ r5 := p->YcoeffsB[0] - p->buf[YADAPTCOEFFSB] + sub r6, r6, r10 @ r6 := p->YcoeffsB[1] - p->buf[YADAPTCOEFFSB-1] + sub r9, r9, r2 @ r9 := p->YcoeffsB[4] - p->buf[YADAPTCOEFFSB-4] + sub r8, r8, r3 @ r8 := p->YcoeffsB[3] - p->buf[YADAPTCOEFFSB-3] + sub r7, r7, r4 @ r7 := p->YcoeffsB[2] - p->buf[YADAPTCOEFFSB-2] + + add r0, r12, #YcoeffsB + stmia r0, {r5 - r9} @ Save p->YcoeffsB[] + + add r1, r12, #YcoeffsA + ldmia r1, {r2 - r5} @ r2 := p->YcoeffsA[0] + @ r3 := p->YcoeffsA[1] + @ r4 := p->YcoeffsA[2] + @ r5 := p->YcoeffsA[3] + + add r6, r14, #YADAPTCOEFFSA-12 + ldmia r6, {r6 - r9} @ r6 := p->buf[YADAPTCOEFFSA-3] + @ r7 := p->buf[YADAPTCOEFFSA-2] + @ r8 := p->buf[YADAPTCOEFFSA-1] + @ r9 := p->buf[YADAPTCOEFFSA] + + sub r5, r5, r6 @ r5 := p->YcoeffsA[3] - p->buf[YADAPTCOEFFSA-3] + sub r4, r4, r7 @ r4 := p->YcoeffsA[2] - p->buf[YADAPTCOEFFSA-2] + sub r3, r3, r8 @ r3 := p->YcoeffsA[1] - p->buf[YADAPTCOEFFSA-1] + sub r2, r2, r9 @ r2 := p->YcoeffsA[0] - p->buf[YADAPTCOEFFSA] + + b 2f + + +1: @ *decoded0 < 0 + + add r5, r5, r11 @ r5 := p->YcoeffsB[0] + p->buf[YADAPTCOEFFSB] + add r6, r6, r10 @ r6 := p->YcoeffsB[1] + p->buf[YADAPTCOEFFSB-1] + add r9, r9, r2 @ r9 := p->YcoeffsB[4] + p->buf[YADAPTCOEFFSB-4] + add r8, r8, r3 @ r9 := p->YcoeffsB[3] + p->buf[YADAPTCOEFFSB-3] + add r7, r7, r4 @ r8 := p->YcoeffsB[2] + p->buf[YADAPTCOEFFSB-2] + + add r0, r12, #YcoeffsB + stmia r0, {r5 - r9} @ Save p->YcoeffsB[] + + add r1, r12, #YcoeffsA + ldmia r1, {r2 - r5} @ r2 := p->YcoeffsA[0] + @ r3 := p->YcoeffsA[1] + @ r4 := p->YcoeffsA[2] + @ r5 := p->YcoeffsA[3] + + add r6, r14, #YADAPTCOEFFSA-12 + ldmia r6, {r6 - r9} @ r6 := p->buf[YADAPTCOEFFSA-3] + @ r7 := p->buf[YADAPTCOEFFSA-2] + @ r8 := p->buf[YADAPTCOEFFSA-1] + @ r9 := p->buf[YADAPTCOEFFSA] + + add r5, r5, r6 @ r5 := p->YcoeffsA[3] + p->buf[YADAPTCOEFFSA-3] + add r4, r4, r7 @ r4 := p->YcoeffsA[2] + p->buf[YADAPTCOEFFSA-2] + add r3, r3, r8 @ r3 := p->YcoeffsA[1] + p->buf[YADAPTCOEFFSA-1] + add r2, r2, r9 @ r2 := p->YcoeffsA[0] + p->buf[YADAPTCOEFFSA] + +2: + stmia r1, {r2 - r5} @ Save p->YcoeffsA + +3: + +@@@@@@@@@@@@@@@@@@@@@@@@@@@ PREDICTOR X + +@ Predictor X, Filter A + + ldr r11, [r12, #XlastA] @ r11 := p->XlastA + + add r2, r14, #XDELAYA-12 @ r2 := &p->buf[XDELAYA-3] + ldmia r2, {r2, r3, r10} @ r2 := p->buf[XDELAYA-3] + @ r3 := p->buf[XDELAYA-2] + @ r10 := p->buf[XDELAYA-1] + + add r6, r12, #XcoeffsA + ldmia r6, {r6 - r9} @ r6 := p->XcoeffsA[0] + @ r7 := p->XcoeffsA[1] + @ r8 := p->XcoeffsA[2] + @ r9 := p->XcoeffsA[3] + + subs r10, r11, r10 @ r10 := r11 - r10 + + STR2OFS r10, r11, r14, #XDELAYA-4 + @ p->buf[XDELAYA-1] = r10 + @ p->buf[XDELAYA] = r11 + + mul r0, r11, r6 @ r0 := p->buf[XDELAYA] * p->XcoeffsA[0] + mla r0, r10, r7, r0 @ r0 += p->buf[XDELAYA-1] * p->XcoeffsA[1] + mla r0, r3, r8, r0 @ r0 += p->buf[XDELAYA-2] * p->XcoeffsA[2] + mla r0, r2, r9, r0 @ r0 += p->buf[XDELAYA-3] * p->XcoeffsA[3] + + @ flags were set above, in the subs instruction + mvngt r10, #0 + movlt r10, #1 @ r10 := SIGN(r10) (see .c for SIGN macro) + + cmp r11, #0 + mvngt r11, #0 + movlt r11, #1 @ r11 := SIGN(r11) (see .c for SIGN macro) + + STR2OFS r10, r11, r14, #XADAPTCOEFFSA-4 + @ p->buf[XADAPTCOEFFSA-1] := r10 + @ p->buf[XADAPTCOEFFSA] := r11 + + @ NOTE: r0 now contains predictionA - don't overwrite. + +@ Predictor X, Filter B + + LDR2OFS r6, r7, r12, #XfilterB @ r6 := p->XfilterB + @ r7 := p->YfilterA + + add r2, r14, #XDELAYB-16 @ r2 := &p->buf[XDELAYB-4] + ldmia r2, {r2 - r4, r10} @ r2 := p->buf[XDELAYB-4] + @ r3 := p->buf[XDELAYB-3] + @ r4 := p->buf[XDELAYB-2] + @ r10 := p->buf[XDELAYB-1] + + rsb r6, r6, r6, lsl #5 @ r6 := r2 * 32 - r6 ( == r6*31) + sub r11, r7, r6, asr #5 @ r11 (p->buf[XDELAYB]) := r7 - (r6 >> 5) + + str r7, [r12, #XfilterB] @ p->XfilterB := r7 (p->YfilterA) + + add r5, r12, #XcoeffsB + ldmia r5, {r5 - r9} @ r5 := p->XcoeffsB[0] + @ r6 := p->XcoeffsB[1] + @ r7 := p->XcoeffsB[2] + @ r8 := p->XcoeffsB[3] + @ r9 := p->XcoeffsB[4] + + subs r10, r11, r10 @ r10 := r11 - r10 + + STR2OFS r10, r11, r14, #XDELAYB-4 + @ p->buf[XDELAYB-1] = r10 + @ p->buf[XDELAYB] = r11 + + mul r1, r11, r5 @ r1 := p->buf[XDELAYB] * p->XcoeffsB[0] + mla r1, r10, r6, r1 @ r1 += p->buf[XDELAYB-1] * p->XcoeffsB[1] + mla r1, r4, r7, r1 @ r1 += p->buf[XDELAYB-2] * p->XcoeffsB[2] + mla r1, r3, r8, r1 @ r1 += p->buf[XDELAYB-3] * p->XcoeffsB[3] + mla r1, r2, r9, r1 @ r1 += p->buf[XDELAYB-4] * p->XcoeffsB[4] + + @ flags were set above, in the subs instruction + mvngt r10, #0 + movlt r10, #1 @ r10 := SIGN(r10) (see .c for SIGN macro) + + cmp r11, #0 + mvngt r11, #0 + movlt r11, #1 @ r11 := SIGN(r11) (see .c for SIGN macro) + + STR2OFS r10, r11, r14, #XADAPTCOEFFSB-4 + @ p->buf[XADAPTCOEFFSB-1] := r10 + @ p->buf[XADAPTCOEFFSB] := r11 + + @ r0 still contains predictionA + @ r1 contains predictionB + + @ Finish Predictor X + + ldr r2, [sp, #4] @ r2 := decoded1 + add r0, r0, r1, asr #1 @ r0 := r0 + (r1 >> 1) + ldr r4, [r12, #XfilterA] @ r4 := p->XfilterA + ldr r3, [r2] @ r3 := *decoded1 + rsb r4, r4, r4, lsl #5 @ r4 := r4 * 32 - r4 ( == r4*31) + add r1, r3, r0, asr #10 @ r1 := r3 + (r0 >> 10) + str r1, [r12, #XlastA] @ p->XlastA := r1 + add r1, r1, r4, asr #5 @ r1 := r1 + (r4 >> 5) + str r1, [r12, #XfilterA] @ p->XfilterA := r1 + + @ r1 contains p->XfilterA + @ r2 contains decoded1 + @ r3 contains *decoded1 + + @ r5, r6, r7, r8, r9 contain p->XcoeffsB[0..4] + @ r10, r11 contain p->buf[XADAPTCOEFFSB-1] and p->buf[XADAPTCOEFFSB] + + str r1, [r2], #4 @ *(decoded1++) := r1 (p->XfilterA) + str r2, [sp, #4] @ save decoded1 + cmp r3, #0 + beq 3f + + add r2, r14, #XADAPTCOEFFSB-16 + ldmia r2, {r2 - r4} @ r2 := p->buf[XADAPTCOEFFSB-4] + @ r3 := p->buf[XADAPTCOEFFSB-3] + @ r4 := p->buf[XADAPTCOEFFSB-2] + blt 1f + + @ *decoded1 > 0 + + sub r5, r5, r11 @ r5 := p->XcoeffsB[0] - p->buf[XADAPTCOEFFSB] + sub r6, r6, r10 @ r6 := p->XcoeffsB[1] - p->buf[XADAPTCOEFFSB-1] + sub r9, r9, r2 @ r9 := p->XcoeffsB[4] - p->buf[XADAPTCOEFFSB-4] + sub r8, r8, r3 @ r8 := p->XcoeffsB[3] - p->buf[XADAPTCOEFFSB-3] + sub r7, r7, r4 @ r7 := p->XcoeffsB[2] - p->buf[XADAPTCOEFFSB-2] + + add r0, r12, #XcoeffsB + stmia r0, {r5 - r9} @ Save p->XcoeffsB[] + + add r1, r12, #XcoeffsA + ldmia r1, {r2 - r5} @ r2 := p->XcoeffsA[0] + @ r3 := p->XcoeffsA[1] + @ r4 := p->XcoeffsA[2] + @ r5 := p->XcoeffsA[3] + + add r6, r14, #XADAPTCOEFFSA-12 + ldmia r6, {r6 - r9} @ r6 := p->buf[XADAPTCOEFFSA-3] + @ r7 := p->buf[XADAPTCOEFFSA-2] + @ r8 := p->buf[XADAPTCOEFFSA-1] + @ r9 := p->buf[XADAPTCOEFFSA] + + sub r5, r5, r6 @ r5 := p->XcoeffsA[3] - p->buf[XADAPTCOEFFSA-3] + sub r4, r4, r7 @ r4 := p->XcoeffsA[2] - p->buf[XADAPTCOEFFSA-2] + sub r3, r3, r8 @ r3 := p->XcoeffsA[1] - p->buf[XADAPTCOEFFSA-1] + sub r2, r2, r9 @ r2 := p->XcoeffsA[0] - p->buf[XADAPTCOEFFSA] + + b 2f + + +1: @ *decoded1 < 0 + + add r5, r5, r11 @ r5 := p->XcoeffsB[0] + p->buf[XADAPTCOEFFSB] + add r6, r6, r10 @ r6 := p->XcoeffsB[1] + p->buf[XADAPTCOEFFSB-1] + add r9, r9, r2 @ r9 := p->XcoeffsB[4] + p->buf[XADAPTCOEFFSB-4] + add r8, r8, r3 @ r8 := p->XcoeffsB[3] + p->buf[XADAPTCOEFFSB-3] + add r7, r7, r4 @ r7 := p->XcoeffsB[2] + p->buf[XADAPTCOEFFSB-2] + + add r0, r12, #XcoeffsB + stmia r0, {r5 - r9} @ Save p->XcoeffsB[] + + add r1, r12, #XcoeffsA + ldmia r1, {r2 - r5} @ r2 := p->XcoeffsA[0] + @ r3 := p->XcoeffsA[1] + @ r4 := p->XcoeffsA[2] + @ r5 := p->XcoeffsA[3] + + add r6, r14, #XADAPTCOEFFSA-12 + ldmia r6, {r6 - r9} @ r6 := p->buf[XADAPTCOEFFSA-3] + @ r7 := p->buf[XADAPTCOEFFSA-2] + @ r8 := p->buf[XADAPTCOEFFSA-1] + @ r9 := p->buf[XADAPTCOEFFSA] + + add r5, r5, r6 @ r5 := p->XcoeffsA[3] + p->buf[XADAPTCOEFFSA-3] + add r4, r4, r7 @ r4 := p->XcoeffsA[2] + p->buf[XADAPTCOEFFSA-2] + add r3, r3, r8 @ r3 := p->XcoeffsA[1] + p->buf[XADAPTCOEFFSA-1] + add r2, r2, r9 @ r2 := p->XcoeffsA[0] + p->buf[XADAPTCOEFFSA] + +2: + stmia r1, {r2 - r5} @ Save p->XcoeffsA + +3: + +@@@@@@@@@@@@@@@@@@@@@@@@@@@ COMMON + + add r14, r14, #4 @ p->buf++ + + add r11, r12, #historybuffer @ r11 := &p->historybuffer[0] + + sub r10, r14, #PREDICTOR_HISTORY_SIZE*4 + @ r10 := p->buf - PREDICTOR_HISTORY_SIZE + + ldr r0, [sp, #8] + cmp r10, r11 + beq move_hist @ The history buffer is full, we need to do a memmove + + @ Check loop count + subs r0, r0, #1 + strne r0, [sp, #8] + bne loop + +done: + str r14, [r12] @ Save value of p->buf + add sp, sp, #12 @ Don't bother restoring r1-r3 +#ifdef ROCKBOX + ldmpc regs=r4-r11 +#else + ldmia sp!, {r4 - r11, pc} +#endif + +move_hist: + @ dest = r11 (p->historybuffer) + @ src = r14 (p->buf) + @ n = 200 + + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + + ldr r0, [sp, #8] + add r14, r12, #historybuffer @ p->buf = &p->historybuffer[0] + + @ Check loop count + subs r0, r0, #1 + strne r0, [sp, #8] + bne loop + + b done + .size predictor_decode_stereo, .-predictor_decode_stereo + + .global predictor_decode_mono + .type predictor_decode_mono,%function + +@ Register usage: +@ +@ r0-r11 - scratch +@ r12 - struct predictor_t* p +@ r14 - int32_t* p->buf + +@ void predictor_decode_mono(struct predictor_t* p, +@ int32_t* decoded0, +@ int count) + +predictor_decode_mono: + stmdb sp!, {r1, r2, r4-r11, lr} + + @ r1 (decoded0) is [sp] + @ r2 (count) is [sp, #4] + + mov r12, r0 @ r12 := p + ldr r14, [r0] @ r14 := p->buf + +loopm: + +@@@@@@@@@@@@@@@@@@@@@@@@@@@ PREDICTOR + + ldr r11, [r12, #YlastA] @ r11 := p->YlastA + + add r2, r14, #YDELAYA-12 @ r2 := &p->buf[YDELAYA-3] + ldmia r2, {r2, r3, r10} @ r2 := p->buf[YDELAYA-3] + @ r3 := p->buf[YDELAYA-2] + @ r10 := p->buf[YDELAYA-1] + + add r5, r12, #YcoeffsA @ r5 := &p->YcoeffsA[0] + ldmia r5, {r6 - r9} @ r6 := p->YcoeffsA[0] + @ r7 := p->YcoeffsA[1] + @ r8 := p->YcoeffsA[2] + @ r9 := p->YcoeffsA[3] + + subs r10, r11, r10 @ r10 := r11 - r10 + + STR2OFS r10, r11, r14, #YDELAYA-4 + @ p->buf[YDELAYA-1] = r10 + @ p->buf[YDELAYA] = r11 + + mul r0, r11, r6 @ r0 := p->buf[YDELAYA] * p->YcoeffsA[0] + mla r0, r10, r7, r0 @ r0 += p->buf[YDELAYA-1] * p->YcoeffsA[1] + mla r0, r3, r8, r0 @ r0 += p->buf[YDELAYA-2] * p->YcoeffsA[2] + mla r0, r2, r9, r0 @ r0 += p->buf[YDELAYA-3] * p->YcoeffsA[3] + + @ flags were set above, in the subs instruction + mvngt r10, #0 + movlt r10, #1 @ r10 := SIGN(r10) (see .c for SIGN macro) + + cmp r11, #0 + mvngt r11, #0 + movlt r11, #1 @ r11 := SIGN(r11) (see .c for SIGN macro) + + STR2OFS r10, r11, r14, #YADAPTCOEFFSA-4 + @ p->buf[YADAPTCOEFFSA-1] := r10 + @ p->buf[YADAPTCOEFFSA] := r11 + + ldr r2, [sp] @ r2 := decoded0 + ldr r4, [r12, #YfilterA] @ r4 := p->YfilterA + ldr r3, [r2] @ r3 := *decoded0 + rsb r4, r4, r4, lsl #5 @ r4 := r4 * 32 - r4 ( == r4*31) + add r1, r3, r0, asr #10 @ r1 := r3 + (r0 >> 10) + str r1, [r12, #YlastA] @ p->YlastA := r1 + add r1, r1, r4, asr #5 @ r1 := r1 + (r4 >> 5) + str r1, [r12, #YfilterA] @ p->YfilterA := r1 + + @ r1 contains p->YfilterA + @ r2 contains decoded0 + @ r3 contains *decoded0 + + @ r6, r7, r8, r9 contain p->YcoeffsA[0..3] + @ r10, r11 contain p->buf[YADAPTCOEFFSA-1] and p->buf[YADAPTCOEFFSA] + + str r1, [r2], #4 @ *(decoded0++) := r1 (p->YfilterA) + str r2, [sp] @ save decoded0 + cmp r3, #0 + beq 3f + + LDR2OFS r2, r3, r14, #YADAPTCOEFFSA-12 + @ r2 := p->buf[YADAPTCOEFFSA-3] + @ r3 := p->buf[YADAPTCOEFFSA-2] + blt 1f + + @ *decoded0 > 0 + + sub r6, r6, r11 @ r6 := p->YcoeffsA[0] - p->buf[YADAPTCOEFFSA] + sub r7, r7, r10 @ r7 := p->YcoeffsA[1] - p->buf[YADAPTCOEFFSA-1] + sub r9, r9, r2 @ r9 := p->YcoeffsA[3] - p->buf[YADAPTCOEFFSA-3] + sub r8, r8, r3 @ r8 := p->YcoeffsA[2] - p->buf[YADAPTCOEFFSA-2] + + b 2f + +1: @ *decoded0 < 0 + + add r6, r6, r11 @ r6 := p->YcoeffsA[0] + p->buf[YADAPTCOEFFSA] + add r7, r7, r10 @ r7 := p->YcoeffsA[1] + p->buf[YADAPTCOEFFSA-1] + add r9, r9, r2 @ r9 := p->YcoeffsA[3] + p->buf[YADAPTCOEFFSA-3] + add r8, r8, r3 @ r8 := p->YcoeffsA[2] + p->buf[YADAPTCOEFFSA-2] + +2: + stmia r5, {r6 - r9} @ Save p->YcoeffsA + +3: + +@@@@@@@@@@@@@@@@@@@@@@@@@@@ COMMON + + add r14, r14, #4 @ p->buf++ + + add r11, r12, #historybuffer @ r11 := &p->historybuffer[0] + + sub r10, r14, #PREDICTOR_HISTORY_SIZE*4 + @ r10 := p->buf - PREDICTOR_HISTORY_SIZE + + ldr r0, [sp, #4] + cmp r10, r11 + beq move_histm @ The history buffer is full, we need to do a memmove + + @ Check loop count + subs r0, r0, #1 + strne r0, [sp, #4] + bne loopm + +donem: + str r14, [r12] @ Save value of p->buf + add sp, sp, #8 @ Don't bother restoring r1, r2 +#ifdef ROCKBOX + ldmpc regs=r4-r11 +#else + ldmia sp!, {r4 - r11, pc} +#endif + +move_histm: + @ dest = r11 (p->historybuffer) + @ src = r14 (p->buf) + @ n = 200 + + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + ldmia r14!, {r0-r9} @ 40 bytes + stmia r11!, {r0-r9} + + ldr r0, [sp, #4] + add r14, r12, #historybuffer @ p->buf = &p->historybuffer[0] + + @ Check loop count + subs r0, r0, #1 + strne r0, [sp, #4] + bne loopm + + b donem + .size predictor_decode_mono, .-predictor_decode_mono diff --git a/lib/rbcodec/codecs/demac/libdemac/predictor-cf.S b/lib/rbcodec/codecs/demac/libdemac/predictor-cf.S new file mode 100644 index 0000000000..fc1d901a59 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/predictor-cf.S @@ -0,0 +1,660 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +Coldfire predictor copyright (C) 2007 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include "demac_config.h" + +/* NOTE: The following need to be kept in sync with parser.h */ + +#define YDELAYA 200 +#define YDELAYB 168 +#define XDELAYA 136 +#define XDELAYB 104 +#define YADAPTCOEFFSA 72 +#define XADAPTCOEFFSA 56 +#define YADAPTCOEFFSB 40 +#define XADAPTCOEFFSB 20 + +/* struct predictor_t members: */ +#define buf 0 /* int32_t* buf */ + +#define YlastA 4 /* int32_t YlastA; */ +#define XlastA 8 /* int32_t XlastA; */ + +#define YfilterB 12 /* int32_t YfilterB; */ +#define XfilterA 16 /* int32_t XfilterA; */ + +#define XfilterB 20 /* int32_t XfilterB; */ +#define YfilterA 24 /* int32_t YfilterA; */ + +#define YcoeffsA 28 /* int32_t YcoeffsA[4]; */ +#define XcoeffsA 44 /* int32_t XcoeffsA[4]; */ +#define YcoeffsB 60 /* int32_t YcoeffsB[5]; */ +#define XcoeffsB 80 /* int32_t XcoeffsB[5]; */ + +#define historybuffer 100 /* int32_t historybuffer[] */ + + + .text + + .align 2 + + .global predictor_decode_stereo + .type predictor_decode_stereo,@function + +| void predictor_decode_stereo(struct predictor_t* p, +| int32_t* decoded0, +| int32_t* decoded1, +| int count) + +predictor_decode_stereo: + lea.l (-12*4,%sp), %sp + movem.l %d2-%d7/%a2-%a6, (4,%sp) + + movem.l (12*4+8,%sp), %a3-%a5 | %a3 = decoded0 + | %a4 = decoded1 + move.l %a5, (%sp) | (%sp) = count + + move.l #0, %macsr | signed integer mode + move.l (12*4+4,%sp), %a6 | %a6 = p + move.l (%a6), %a5 | %a5 = p->buf + +.loop: + + | ***** PREDICTOR Y ***** + + | Predictor Y, Filter A + + move.l (YlastA,%a6), %d3 | %d3 = p->YlastA + + movem.l (YDELAYA-12,%a5), %d0-%d2 | %d0 = p->buf[YDELAYA-3] + | %d1 = p->buf[YDELAYA-2] + | %d2 = p->buf[YDELAYA-1] + + move.l %d3, (YDELAYA,%a5) | p->buf[YDELAYA] = %d3 + + sub.l %d3, %d2 + neg.l %d2 | %d2 = %d3 - %d2 + + move.l %d2, (YDELAYA-4,%a5) | p->buf[YDELAYA-1] = %d2 + + movem.l (YcoeffsA,%a6), %d4-%d7 | %d4 = p->YcoeffsA[0] + | %d5 = p->YcoeffsA[1] + | %d6 = p->YcoeffsA[2] + | %d7 = p->YcoeffsA[3] + + mac.l %d3, %d4, %acc0 | %acc0 = p->buf[YDELAYA] * p->YcoeffsA[0] + mac.l %d2, %d5, %acc0 | %acc0 += p->buf[YDELAYA-1] * p->YcoeffsA[1] + mac.l %d1, %d6, %acc0 | %acc0 += p->buf[YDELAYA-2] * p->YcoeffsA[2] + mac.l %d0, %d7, %acc0 | %acc0 += p->buf[YDELAYA-3] * p->YcoeffsA[3] + + tst.l %d2 + beq.s 1f + spl.b %d2 | pos: 0x??????ff, neg: 0x??????00 + extb.l %d2 | pos: 0xffffffff, neg: 0x00000000 + or.l #1, %d2 | pos: 0xffffffff, neg: 0x00000001 +1: | %d2 = SIGN(%d2) + move.l %d2, (YADAPTCOEFFSA-4,%a5) | p->buf[YADAPTCOEFFSA-1] = %d2 + + tst.l %d3 + beq.s 1f + spl.b %d3 + extb.l %d3 + or.l #1, %d3 +1: | %d3 = SIGN(%d3) + move.l %d3, (YADAPTCOEFFSA,%a5) | p->buf[YADAPTCOEFFSA] = %d3 + + | Predictor Y, Filter B + + movem.l (YfilterB,%a6), %d2-%d3 | %d2 = p->YfilterB + | %d3 = p->XfilterA + move.l %d3, (YfilterB,%a6) | p->YfilterB = %d3 + + move.l %d2, %d1 | %d1 = %d2 + lsl.l #5, %d2 | %d2 = %d2 * 32 + sub.l %d1, %d2 | %d2 -= %d1 (== 31 * old_d2) + asr.l #5, %d2 | %d2 >>= 5 + sub.l %d2, %d3 | %d3 -= %d2 + + movem.l (YDELAYB-16,%a5), %d4-%d7 | %d4 = p->buf[YDELAYB-4] + | %d5 = p->buf[YDELAYB-3] + | %d6 = p->buf[YDELAYB-2] + | %d7 = p->buf[YDELAYB-1] + sub.l %d3, %d7 + neg.l %d7 | %d7 = %d3 - %d7 + + move.l %d7, (YDELAYB-4,%a5) | p->buf[YDELAYB-1] = %d7 + + movem.l (YcoeffsB,%a6), %d1-%d2/%a0-%a2 | %d1 = p->YcoeffsB[0] + | %d2 = p->YcoeffsB[1] + | %a0 = p->YcoeffsB[2] + | %a1 = p->YcoeffsB[3] + | %a2 = p->YcoeffsB[4] + + mac.l %d3, %d1, %acc1 | %acc1 = p->buf[YDELAYB] * p->YcoeffsB[0] + mac.l %d7, %d2, %acc1 | %acc1 += p->buf[YDELAYB-1] * p->YcoeffsB[1] + mac.l %d6, %a0, %acc1 | %acc1 += p->buf[YDELAYB-2] * p->YcoeffsB[2] + mac.l %d5, %a1, %acc1 | %acc1 += p->buf[YDELAYB-3] * p->YcoeffsB[3] + mac.l %d4, %a2, %acc1 | %acc1 += p->buf[YDELAYB-4] * p->YcoeffsB[4] + + move.l %d3, (YDELAYB, %a5) | p->buf[YDELAYB] = %d3 + + tst.l %d7 + beq.s 1f + spl.b %d7 + extb.l %d7 + or.l #1, %d7 +1: | %d7 = SIGN(%d7) + move.l %d7, (YADAPTCOEFFSB-4,%a5) | p->buf[YADAPTCOEFFSB-1] = %d7 + tst.l %d3 + beq.s 1f + spl.b %d3 + extb.l %d3 + or.l #1, %d3 +1: | %d3 = SIGN(%d3) + move.l %d3, (YADAPTCOEFFSB, %a5) | p->buf[YADAPTCOEFFSB] = %d3 + + | %d1, %d2, %a0, %a1, %a2 contain p->YcoeffsB[0..4] + | %d7, %d3 contain p->buf[YADAPTCOEFFSB-1] and p->buf[YADAPTCOEFFSB] + + move.l (%a3), %d0 | %d0 = *decoded0 + beq.s 3f + + movem.l (YADAPTCOEFFSB-16,%a5), %d4-%d6 | %d4 = p->buf[YADAPTCOEFFSB-4] + | %d5 = p->buf[YADAPTCOEFFSB-3] + | %d6 = p->buf[YADAPTCOEFFSB-2] + + bmi.s 1f | flags still valid here + + | *decoded0 > 0 + + sub.l %d3, %d1 | %d1 = p->YcoeffsB[0] - p->buf[YADAPTCOEFFSB] + sub.l %d7, %d2 | %d2 = p->YcoeffsB[1] - p->buf[YADAPTCOEFFSB-1] + sub.l %d6, %a0 | %a0 = p->YcoeffsB[2] - p->buf[YADAPTCOEFFSB-2] + sub.l %d5, %a1 | %a1 = p->YcoeffsB[3] - p->buf[YADAPTCOEFFSB-3] + sub.l %d4, %a2 | %a2 = p->YcoeffsB[4] - p->buf[YADAPTCOEFFSB-4] + + movem.l %d1-%d2/%a0-%a2, (YcoeffsB,%a6) | Save p->YcoeffsB[] + + movem.l (YcoeffsA,%a6), %d4-%d7 | %d4 = p->YcoeffsA[0] + | %d5 = p->YcoeffsA[1] + | %d6 = p->YcoeffsA[2] + | %d7 = p->YcoeffsA[3] + + movem.l (YADAPTCOEFFSA-12,%a5), %d2/%a0-%a2 + | %d2 = p->buf[YADAPTCOEFFSA-3] + | %a0 = p->buf[YADAPTCOEFFSA-2] + | %a1 = p->buf[YADAPTCOEFFSA-1] + | %a2 = p->buf[YADAPTCOEFFSA] + + sub.l %a2, %d4 | %d4 = p->YcoeffsA[0] - p->buf[YADAPTCOEFFSA] + sub.l %a1, %d5 | %d5 = p->YcoeffsA[1] - p->buf[YADAPTCOEFFSA-1] + sub.l %a0, %d6 | %d6 = p->YcoeffsA[2] - p->buf[YADAPTCOEFFSA-2] + sub.l %d2, %d7 | %d7 = p->YcoeffsA[3] - p->buf[YADAPTCOEFFSA-3] + + bra.s 2f + +1: | *decoded0 < 0 + + add.l %d3, %d1 | %d1 = p->YcoeffsB[0] + p->buf[YADAPTCOEFFSB] + add.l %d7, %d2 | %d2 = p->YcoeffsB[1] + p->buf[YADAPTCOEFFSB-1] + add.l %d6, %a0 | %a0 = p->YcoeffsB[2] + p->buf[YADAPTCOEFFSB-2] + add.l %d5, %a1 | %a1 = p->YcoeffsB[3] + p->buf[YADAPTCOEFFSB-3] + add.l %d4, %a2 | %a2 = p->YcoeffsB[4] + p->buf[YADAPTCOEFFSB-4] + + movem.l %d1-%d2/%a0-%a2, (YcoeffsB,%a6) | Save p->YcoeffsB[] + + movem.l (YcoeffsA,%a6), %d4-%d7 | %d4 = p->YcoeffsA[0] + | %d5 = p->YcoeffsA[1] + | %d6 = p->YcoeffsA[2] + | %d7 = p->YcoeffsA[3] + + movem.l (YADAPTCOEFFSA-12,%a5), %d2/%a0-%a2 + | %d2 = p->buf[YADAPTCOEFFSA-3] + | %a0 = p->buf[YADAPTCOEFFSA-2] + | %a1 = p->buf[YADAPTCOEFFSA-1] + | %a2 = p->buf[YADAPTCOEFFSA] + + add.l %a2, %d4 | %d4 = p->YcoeffsA[0] + p->buf[YADAPTCOEFFSA] + add.l %a1, %d5 | %d5 = p->YcoeffsA[1] + p->buf[YADAPTCOEFFSA-1] + add.l %a0, %d6 | %d6 = p->YcoeffsA[2] + p->buf[YADAPTCOEFFSA-2] + add.l %d2, %d7 | %d7 = p->YcoeffsA[3] + p->buf[YADAPTCOEFFSA-3] + +2: + movem.l %d4-%d7, (YcoeffsA,%a6) | Save p->YcoeffsA[] + +3: + | Finish Predictor Y + + movclr.l %acc0, %d1 | %d1 = predictionA + movclr.l %acc1, %d2 | %d2 = predictionB + asr.l #1, %d2 + add.l %d2, %d1 | %d1 += (%d2 >> 1) + asr.l #8, %d1 + asr.l #2, %d1 | %d1 >>= 10 + add.l %d0, %d1 | %d1 += %d0 + move.l %d1, (YlastA,%a6) | p->YlastA = %d1 + + move.l (YfilterA,%a6), %d2 | %d2 = p->YfilterA + move.l %d2, %d0 + lsl.l #5, %d2 + sub.l %d0, %d2 | %d2 = 31 * %d2 + asr.l #5, %d2 | %d2 >>= 5 + add.l %d1, %d2 + move.l %d2, (YfilterA,%a6) | p->YfilterA = %d2 + + | *decoded0 stored 2 instructions down, avoiding pipeline stall + + | ***** PREDICTOR X ***** + + | Predictor X, Filter A + + move.l (XlastA,%a6), %d3 | %d3 = p->XlastA + + move.l %d2, (%a3)+ | *(decoded0++) = %d2 (p->YfilterA) + + movem.l (XDELAYA-12,%a5), %d0-%d2 | %d0 = p->buf[XDELAYA-3] + | %d1 = p->buf[XDELAYA-2] + | %d2 = p->buf[XDELAYA-1] + + move.l %d3, (XDELAYA,%a5) | p->buf[XDELAYA] = %d3 + + sub.l %d3, %d2 + neg.l %d2 | %d2 = %d3 -%d2 + + move.l %d2, (XDELAYA-4,%a5) | p->buf[XDELAYA-1] = %d2 + + movem.l (XcoeffsA,%a6), %d4-%d7 | %d4 = p->XcoeffsA[0] + | %d5 = p->XcoeffsA[1] + | %d6 = p->XcoeffsA[2] + | %d7 = p->XcoeffsA[3] + + mac.l %d3, %d4, %acc0 | %acc0 = p->buf[XDELAYA] * p->XcoeffsA[0] + mac.l %d2, %d5, %acc0 | %acc0 += p->buf[XDELAYA-1] * p->XcoeffsA[1] + mac.l %d1, %d6, %acc0 | %acc0 += p->buf[XDELAYA-2] * p->XcoeffsA[2] + mac.l %d0, %d7, %acc0 | %acc0 += p->buf[XDELAYA-3] * p->XcoeffsA[3] + + tst.l %d2 + beq.s 1f + spl.b %d2 | pos: 0x??????ff, neg: 0x??????00 + extb.l %d2 | pos: 0xffffffff, neg: 0x00000000 + or.l #1, %d2 | pos: 0xffffffff, neg: 0x00000001 +1: | %d2 = SIGN(%d2) + move.l %d2, (XADAPTCOEFFSA-4,%a5) | p->buf[XADAPTCOEFFSA-1] = %d2 + + tst.l %d3 + beq.s 1f + spl.b %d3 + extb.l %d3 + or.l #1, %d3 +1: | %d3 = SIGN(%d3) + move.l %d3, (XADAPTCOEFFSA,%a5) | p->buf[XADAPTCOEFFSA] = %d3 + + | Predictor X, Filter B + + movem.l (XfilterB,%a6), %d2-%d3 | %d2 = p->XfilterB + | %d3 = p->YfilterA + move.l %d3, (XfilterB,%a6) | p->XfilterB = %d3 + + move.l %d2, %d1 | %d1 = %d2 + lsl.l #5, %d2 | %d2 = %d2 * 32 + sub.l %d1, %d2 | %d2 -= %d1 (== 31 * old_d2) + asr.l #5, %d2 | %d2 >>= 5 + sub.l %d2, %d3 | %d3 -= %d2 + + movem.l (XDELAYB-16,%a5), %d4-%d7 | %d4 = p->buf[XDELAYB-4] + | %d5 = p->buf[XDELAYB-3] + | %d6 = p->buf[XDELAYB-2] + | %d7 = p->buf[XDELAYB-1] + sub.l %d3, %d7 + neg.l %d7 | %d7 = %d3 - %d7 + + move.l %d7, (XDELAYB-4,%a5) | p->buf[XDELAYB-1] = %d7 + + movem.l (XcoeffsB,%a6), %d1-%d2/%a0-%a2 | %d1 = p->XcoeffsB[0] + | %d2 = p->XcoeffsB[1] + | %a0 = p->XcoeffsB[2] + | %a1 = p->XcoeffsB[3] + | %a2 = p->XcoeffsB[4] + + mac.l %d3, %d1, %acc1 | %acc1 = p->buf[XDELAYB] * p->XcoeffsB[0] + mac.l %d7, %d2, %acc1 | %acc1 += p->buf[XDELAYB-1] * p->XcoeffsB[1] + mac.l %d6, %a0, %acc1 | %acc1 += p->buf[XDELAYB-2] * p->XcoeffsB[2] + mac.l %d5, %a1, %acc1 | %acc1 += p->buf[XDELAYB-3] * p->XcoeffsB[3] + mac.l %d4, %a2, %acc1 | %acc1 += p->buf[XDELAYB-4] * p->XcoeffsB[4] + + move.l %d3, (XDELAYB, %a5) | p->buf[XDELAYB] = %d3 + + tst.l %d7 + beq.s 1f + spl.b %d7 + extb.l %d7 + or.l #1, %d7 +1: | %d7 = SIGN(%d7) + move.l %d7, (XADAPTCOEFFSB-4,%a5) | p->buf[XADAPTCOEFFSB-1] = %d7 + + tst.l %d3 + beq.s 1f + spl.b %d3 + extb.l %d3 + or.l #1, %d3 +1: | %d3 = SIGN(%d3) + move.l %d3, (XADAPTCOEFFSB, %a5) | p->buf[XADAPTCOEFFSB] = %d3 + + | %d1, %d2, %a0, %a1, %a2 contain p->XcoeffsB[0..4] + | %d7, %d3 contain p->buf[XADAPTCOEFFSB-1] and p->buf[XADAPTCOEFFSB] + + move.l (%a4), %d0 | %d0 = *decoded1 + beq.s 3f + + movem.l (XADAPTCOEFFSB-16,%a5), %d4-%d6 | %d4 = p->buf[XADAPTCOEFFSB-4] + | %d5 = p->buf[XADAPTCOEFFSB-3] + | %d6 = p->buf[XADAPTCOEFFSB-2] + + bmi.s 1f | flags still valid here + + | *decoded1 > 0 + + sub.l %d3, %d1 | %d1 = p->XcoeffsB[0] - p->buf[XADAPTCOEFFSB] + sub.l %d7, %d2 | %d2 = p->XcoeffsB[1] - p->buf[XADAPTCOEFFSB-1] + sub.l %d6, %a0 | %a0 = p->XcoeffsB[2] - p->buf[XADAPTCOEFFSB-2] + sub.l %d5, %a1 | %a1 = p->XcoeffsB[3] - p->buf[XADAPTCOEFFSB-3] + sub.l %d4, %a2 | %a2 = p->XcoeffsB[4] - p->buf[XADAPTCOEFFSB-4] + + movem.l %d1-%d2/%a0-%a2, (XcoeffsB,%a6) | Save p->XcoeffsB[] + + movem.l (XcoeffsA,%a6), %d4-%d7 | %d4 = p->XcoeffsA[0] + | %d5 = p->XcoeffsA[1] + | %d6 = p->XcoeffsA[2] + | %d7 = p->XcoeffsA[3] + + movem.l (XADAPTCOEFFSA-12,%a5), %d2/%a0-%a2 + | %d2 = p->buf[XADAPTCOEFFSA-3] + | %a0 = p->buf[XADAPTCOEFFSA-2] + | %a1 = p->buf[XADAPTCOEFFSA-1] + | %a2 = p->buf[XADAPTCOEFFSA] + + sub.l %a2, %d4 | %d4 = p->XcoeffsA[0] - p->buf[XADAPTCOEFFSA] + sub.l %a1, %d5 | %d5 = p->XcoeffsA[1] - p->buf[XADAPTCOEFFSA-1] + sub.l %a0, %d6 | %d6 = p->XcoeffsA[2] - p->buf[XADAPTCOEFFSA-2] + sub.l %d2, %d7 | %d7 = p->XcoeffsA[3] - p->buf[XADAPTCOEFFSA-3] + + bra.s 2f + +1: | *decoded1 < 0 + + add.l %d3, %d1 | %d1 = p->XcoeffsB[0] + p->buf[XADAPTCOEFFSB] + add.l %d7, %d2 | %d2 = p->XcoeffsB[1] + p->buf[XADAPTCOEFFSB-1] + add.l %d6, %a0 | %a0 = p->XcoeffsB[2] + p->buf[XADAPTCOEFFSB-2] + add.l %d5, %a1 | %a1 = p->XcoeffsB[3] + p->buf[XADAPTCOEFFSB-3] + add.l %d4, %a2 | %a2 = p->XcoeffsB[4] + p->buf[XADAPTCOEFFSB-4] + + movem.l %d1-%d2/%a0-%a2, (XcoeffsB,%a6) | Save p->XcoeffsB[] + + movem.l (XcoeffsA,%a6), %d4-%d7 | %d4 = p->XcoeffsA[0] + | %d5 = p->XcoeffsA[1] + | %d6 = p->XcoeffsA[2] + | %d7 = p->XcoeffsA[3] + + movem.l (XADAPTCOEFFSA-12,%a5), %d2/%a0-%a2 + | %d2 = p->buf[XADAPTCOEFFSA-3] + | %a0 = p->buf[XADAPTCOEFFSA-2] + | %a1 = p->buf[XADAPTCOEFFSA-1] + | %a2 = p->buf[XADAPTCOEFFSA] + + add.l %a2, %d4 | %d4 = p->XcoeffsA[0] + p->buf[XADAPTCOEFFSA] + add.l %a1, %d5 | %d5 = p->XcoeffsA[1] + p->buf[XADAPTCOEFFSA-1] + add.l %a0, %d6 | %d6 = p->XcoeffsA[2] + p->buf[XADAPTCOEFFSA-2] + add.l %d2, %d7 | %d7 = p->XcoeffsA[3] + p->buf[XADAPTCOEFFSA-3] + +2: + movem.l %d4-%d7, (XcoeffsA,%a6) | Save p->XcoeffsA[] + +3: + | Finish Predictor X + + movclr.l %acc0, %d1 | %d1 = predictionA + movclr.l %acc1, %d2 | %d2 = predictionB + asr.l #1, %d2 + add.l %d2, %d1 | %d1 += (%d2 >> 1) + asr.l #8, %d1 + asr.l #2, %d1 | %d1 >>= 10 + add.l %d0, %d1 | %d1 += %d0 + move.l %d1, (XlastA,%a6) | p->XlastA = %d1 + + move.l (XfilterA,%a6), %d2 | %d2 = p->XfilterA + move.l %d2, %d0 + lsl.l #5, %d2 + sub.l %d0, %d2 | %d2 = 31 * %d2 + asr.l #5, %d2 | %d6 >>= 2 + add.l %d1, %d2 + move.l %d2, (XfilterA,%a6) | p->XfilterA = %d2 + + | *decoded1 stored 3 instructions down, avoiding pipeline stall + + | ***** COMMON ***** + + addq.l #4, %a5 | p->buf++ + lea.l (historybuffer+PREDICTOR_HISTORY_SIZE*4,%a6), %a2 + | %a2 = &p->historybuffer[PREDICTOR_HISTORY_SIZE] + + move.l %d2, (%a4)+ | *(decoded1++) = %d2 (p->XfilterA) + + cmp.l %a2, %a5 + beq.s .move_hist | History buffer is full, we need to do a memmove + + subq.l #1, (%sp) | decrease loop count + bne.w .loop + +.done: + move.l %a5, (%a6) | Save value of p->buf + movem.l (4,%sp), %d2-%d7/%a2-%a6 + lea.l (12*4,%sp), %sp + rts + +.move_hist: + lea.l (historybuffer,%a6), %a2 + + | dest = %a2 (p->historybuffer) + | src = %a5 (p->buf) + | n = 200 + + movem.l (%a5), %d0-%d7/%a0-%a1 | 40 bytes + movem.l %d0-%d7/%a0-%a1, (%a2) + movem.l (40,%a5), %d0-%d7/%a0-%a1 | 40 bytes + movem.l %d0-%d7/%a0-%a1, (40,%a2) + movem.l (80,%a5), %d0-%d7/%a0-%a1 | 40 bytes + movem.l %d0-%d7/%a0-%a1, (80,%a2) + movem.l (120,%a5), %d0-%d7/%a0-%a1 | 40 bytes + movem.l %d0-%d7/%a0-%a1, (120,%a2) + movem.l (160,%a5), %d0-%d7/%a0-%a1 | 40 bytes + movem.l %d0-%d7/%a0-%a1, (160,%a2) + + move.l %a2, %a5 | p->buf = &p->historybuffer[0] + + subq.l #1, (%sp) | decrease loop count + bne.w .loop + + bra.s .done + .size predictor_decode_stereo, .-predictor_decode_stereo + + + .global predictor_decode_mono + .type predictor_decode_mono,@function + +| void predictor_decode_mono(struct predictor_t* p, +| int32_t* decoded0, +| int count) + +predictor_decode_mono: + lea.l (-11*4,%sp), %sp + movem.l %d2-%d7/%a2-%a6, (%sp) + + move.l #0, %macsr | signed integer mode + + move.l (11*4+4,%sp), %a6 | %a6 = p + move.l (11*4+8,%sp), %a4 | %a4 = decoded0 + move.l (11*4+12,%sp), %d7 | %d7 = count + move.l (%a6), %a5 | %a5 = p->buf + + move.l (YlastA,%a6), %d3 | %d3 = p->YlastA + +.loopm: + + | ***** PREDICTOR ***** + + movem.l (YDELAYA-12,%a5), %d0-%d2 | %d0 = p->buf[YDELAYA-3] + | %d1 = p->buf[YDELAYA-2] + | %d2 = p->buf[YDELAYA-1] + + move.l %d3, (YDELAYA,%a5) | p->buf[YDELAYA] = %d3 + + sub.l %d3, %d2 + neg.l %d2 | %d2 = %d3 - %d2 + + move.l %d2, (YDELAYA-4,%a5) | p->buf[YDELAYA-1] = %d2 + + movem.l (YcoeffsA,%a6), %a0-%a3 | %a0 = p->YcoeffsA[0] + | %a1 = p->YcoeffsA[1] + | %a2 = p->YcoeffsA[2] + | %a3 = p->YcoeffsA[3] + + mac.l %d3, %a0, %acc0 | %acc0 = p->buf[YDELAYA] * p->YcoeffsA[0] + mac.l %d2, %a1, %acc0 | %acc0 += p->buf[YDELAYA-1] * p->YcoeffsA[1] + mac.l %d1, %a2, %acc0 | %acc0 += p->buf[YDELAYA-2] * p->YcoeffsA[2] + mac.l %d0, %a3, %acc0 | %acc0 += p->buf[YDELAYA-3] * p->YcoeffsA[3] + + tst.l %d2 + beq.s 1f + spl.b %d2 | pos: 0x??????ff, neg: 0x??????00 + extb.l %d2 | pos: 0xffffffff, neg: 0x00000000 + or.l #1, %d2 | pos: 0xffffffff, neg: 0x00000001 +1: | %d2 = SIGN(%d2) + move.l %d2, (YADAPTCOEFFSA-4,%a5) | p->buf[YADAPTCOEFFSA-1] = %d2 + + tst.l %d3 + beq.s 1f + spl.b %d3 + extb.l %d3 + or.l #1, %d3 +1: | %d3 = SIGN(%d3) + move.l %d3, (YADAPTCOEFFSA,%a5) | p->buf[YADAPTCOEFFSA] = %d3 + + move.l (%a4), %d0 | %d0 = *decoded0 + beq.s 3f + + movem.l (YADAPTCOEFFSA-12,%a5),%d4-%d5 | %d4 = p->buf[YADAPTCOEFFSA-3] + | %d5 = p->buf[YADAPTCOEFFSA-2] + + bmi.s 1f | flags still valid here + + | *decoded0 > 0 + + sub.l %d3, %a0 | %a0 = p->YcoeffsA[0] - p->buf[YADAPTCOEFFSA] + sub.l %d2, %a1 | %a1 = p->YcoeffsA[1] - p->buf[YADAPTCOEFFSA-1] + sub.l %d5, %a2 | %a2 = p->YcoeffsA[2] - p->buf[YADAPTCOEFFSA-2] + sub.l %d4, %a3 | %a3 = p->YcoeffsA[3] - p->buf[YADAPTCOEFFSA-3] + + bra.s 2f + +1: | *decoded0 < 0 + + add.l %d3, %a0 | %a0 = p->YcoeffsA[0] - p->buf[YADAPTCOEFFSA] + add.l %d2, %a1 | %a1 = p->YcoeffsA[1] - p->buf[YADAPTCOEFFSA-1] + add.l %d5, %a2 | %a2 = p->YcoeffsA[2] - p->buf[YADAPTCOEFFSA-2] + add.l %d4, %a3 | %a3 = p->YcoeffsA[3] - p->buf[YADAPTCOEFFSA-3] + +2: + movem.l %a0-%a3, (YcoeffsA,%a6) | save p->YcoeffsA[] + +3: + | Finish Predictor + + movclr.l %acc0, %d3 | %d3 = predictionA + asr.l #8, %d3 + asr.l #2, %d3 | %d3 >>= 10 + add.l %d0, %d3 | %d3 += %d0 + + move.l (YfilterA,%a6), %d2 | %d2 = p->YfilterA + move.l %d2, %d0 + lsl.l #5, %d2 + sub.l %d0, %d2 | %d2 = 31 * %d2 + asr.l #5, %d2 | %d2 >>= 5 + add.l %d3, %d2 + move.l %d2, (YfilterA,%a6) | p->YfilterA = %d2 + + | *decoded0 stored 3 instructions down, avoiding pipeline stall + + | ***** COMMON ***** + + addq.l #4, %a5 | p->buf++ + lea.l (historybuffer+PREDICTOR_HISTORY_SIZE*4,%a6), %a3 + | %a3 = &p->historybuffer[PREDICTOR_HISTORY_SIZE] + + move.l %d2, (%a4)+ | *(decoded0++) = %d2 (p->YfilterA) + + cmp.l %a3, %a5 + beq.s .move_histm | History buffer is full, we need to do a memmove + + subq.l #1, %d7 | decrease loop count + bne.w .loopm + + move.l %d3, (YlastA,%a6) | %d3 = p->YlastA + +.donem: + move.l %a5, (%a6) | Save value of p->buf + movem.l (%sp), %d2-%d7/%a2-%a6 + lea.l (11*4,%sp), %sp + rts + +.move_histm: + move.l %d3, (YlastA,%a6) | %d3 = p->YlastA + + lea.l (historybuffer,%a6), %a3 + + | dest = %a3 (p->historybuffer) + | src = %a5 (p->buf) + | n = 200 + + movem.l (%a5), %d0-%d6/%a0-%a2 | 40 bytes + movem.l %d0-%d6/%a0-%a2, (%a3) + movem.l (40,%a5), %d0-%d6/%a0-%a2 | 40 bytes + movem.l %d0-%d6/%a0-%a2, (40,%a3) + movem.l (80,%a5), %d0-%d6/%a0-%a2 | 40 bytes + movem.l %d0-%d6/%a0-%a2, (80,%a3) + movem.l (120,%a5), %d0-%d6/%a0-%a2 | 40 bytes + movem.l %d0-%d6/%a0-%a2, (120,%a3) + movem.l (160,%a5), %d0-%d6/%a0-%a2 | 40 bytes + movem.l %d0-%d6/%a0-%a2, (160,%a3) + + move.l %a3, %a5 | p->buf = &p->historybuffer[0] + + move.l (YlastA,%a6), %d3 | %d3 = p->YlastA + + subq.l #1, %d7 | decrease loop count + bne.w .loopm + + bra.s .donem + .size predictor_decode_mono, .-predictor_decode_mono diff --git a/lib/rbcodec/codecs/demac/libdemac/predictor.c b/lib/rbcodec/codecs/demac/libdemac/predictor.c new file mode 100644 index 0000000000..45912dddbd --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/predictor.c @@ -0,0 +1,271 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <inttypes.h> +#include <string.h> + +#include "parser.h" +#include "predictor.h" +#include "demac_config.h" + +/* Return 0 if x is zero, -1 if x is positive, 1 if x is negative */ +#define SIGN(x) (x) ? (((x) > 0) ? -1 : 1) : 0 + +static const int32_t initial_coeffs[4] = { + 360, 317, -109, 98 +}; + +#define YDELAYA (18 + PREDICTOR_ORDER*4) +#define YDELAYB (18 + PREDICTOR_ORDER*3) +#define XDELAYA (18 + PREDICTOR_ORDER*2) +#define XDELAYB (18 + PREDICTOR_ORDER) + +#define YADAPTCOEFFSA (18) +#define XADAPTCOEFFSA (14) +#define YADAPTCOEFFSB (10) +#define XADAPTCOEFFSB (5) + +void init_predictor_decoder(struct predictor_t* p) +{ + /* Zero the history buffers */ + memset(p->historybuffer, 0, PREDICTOR_SIZE * sizeof(int32_t)); + p->buf = p->historybuffer; + + /* Initialise and zero the co-efficients */ + memcpy(p->YcoeffsA, initial_coeffs, sizeof(initial_coeffs)); + memcpy(p->XcoeffsA, initial_coeffs, sizeof(initial_coeffs)); + memset(p->YcoeffsB, 0, sizeof(p->YcoeffsB)); + memset(p->XcoeffsB, 0, sizeof(p->XcoeffsB)); + + p->YfilterA = 0; + p->YfilterB = 0; + p->YlastA = 0; + + p->XfilterA = 0; + p->XfilterB = 0; + p->XlastA = 0; +} + +#if !defined(CPU_ARM) && !defined(CPU_COLDFIRE) +void ICODE_ATTR_DEMAC predictor_decode_stereo(struct predictor_t* p, + int32_t* decoded0, + int32_t* decoded1, + int count) +{ + int32_t predictionA, predictionB; + + while (LIKELY(count--)) + { + /* Predictor Y */ + p->buf[YDELAYA] = p->YlastA; + p->buf[YADAPTCOEFFSA] = SIGN(p->buf[YDELAYA]); + + p->buf[YDELAYA-1] = p->buf[YDELAYA] - p->buf[YDELAYA-1]; + p->buf[YADAPTCOEFFSA-1] = SIGN(p->buf[YDELAYA-1]); + + predictionA = (p->buf[YDELAYA] * p->YcoeffsA[0]) + + (p->buf[YDELAYA-1] * p->YcoeffsA[1]) + + (p->buf[YDELAYA-2] * p->YcoeffsA[2]) + + (p->buf[YDELAYA-3] * p->YcoeffsA[3]); + + /* Apply a scaled first-order filter compression */ + p->buf[YDELAYB] = p->XfilterA - ((p->YfilterB * 31) >> 5); + p->buf[YADAPTCOEFFSB] = SIGN(p->buf[YDELAYB]); + p->YfilterB = p->XfilterA; + + p->buf[YDELAYB-1] = p->buf[YDELAYB] - p->buf[YDELAYB-1]; + p->buf[YADAPTCOEFFSB-1] = SIGN(p->buf[YDELAYB-1]); + + predictionB = (p->buf[YDELAYB] * p->YcoeffsB[0]) + + (p->buf[YDELAYB-1] * p->YcoeffsB[1]) + + (p->buf[YDELAYB-2] * p->YcoeffsB[2]) + + (p->buf[YDELAYB-3] * p->YcoeffsB[3]) + + (p->buf[YDELAYB-4] * p->YcoeffsB[4]); + + p->YlastA = *decoded0 + ((predictionA + (predictionB >> 1)) >> 10); + p->YfilterA = p->YlastA + ((p->YfilterA * 31) >> 5); + + /* Predictor X */ + + p->buf[XDELAYA] = p->XlastA; + p->buf[XADAPTCOEFFSA] = SIGN(p->buf[XDELAYA]); + p->buf[XDELAYA-1] = p->buf[XDELAYA] - p->buf[XDELAYA-1]; + p->buf[XADAPTCOEFFSA-1] = SIGN(p->buf[XDELAYA-1]); + + predictionA = (p->buf[XDELAYA] * p->XcoeffsA[0]) + + (p->buf[XDELAYA-1] * p->XcoeffsA[1]) + + (p->buf[XDELAYA-2] * p->XcoeffsA[2]) + + (p->buf[XDELAYA-3] * p->XcoeffsA[3]); + + /* Apply a scaled first-order filter compression */ + p->buf[XDELAYB] = p->YfilterA - ((p->XfilterB * 31) >> 5); + p->buf[XADAPTCOEFFSB] = SIGN(p->buf[XDELAYB]); + p->XfilterB = p->YfilterA; + p->buf[XDELAYB-1] = p->buf[XDELAYB] - p->buf[XDELAYB-1]; + p->buf[XADAPTCOEFFSB-1] = SIGN(p->buf[XDELAYB-1]); + + predictionB = (p->buf[XDELAYB] * p->XcoeffsB[0]) + + (p->buf[XDELAYB-1] * p->XcoeffsB[1]) + + (p->buf[XDELAYB-2] * p->XcoeffsB[2]) + + (p->buf[XDELAYB-3] * p->XcoeffsB[3]) + + (p->buf[XDELAYB-4] * p->XcoeffsB[4]); + + p->XlastA = *decoded1 + ((predictionA + (predictionB >> 1)) >> 10); + p->XfilterA = p->XlastA + ((p->XfilterA * 31) >> 5); + + if (LIKELY(*decoded0 != 0)) + { + if (*decoded0 > 0) + { + p->YcoeffsA[0] -= p->buf[YADAPTCOEFFSA]; + p->YcoeffsA[1] -= p->buf[YADAPTCOEFFSA-1]; + p->YcoeffsA[2] -= p->buf[YADAPTCOEFFSA-2]; + p->YcoeffsA[3] -= p->buf[YADAPTCOEFFSA-3]; + + p->YcoeffsB[0] -= p->buf[YADAPTCOEFFSB]; + p->YcoeffsB[1] -= p->buf[YADAPTCOEFFSB-1]; + p->YcoeffsB[2] -= p->buf[YADAPTCOEFFSB-2]; + p->YcoeffsB[3] -= p->buf[YADAPTCOEFFSB-3]; + p->YcoeffsB[4] -= p->buf[YADAPTCOEFFSB-4]; + } + else + { + p->YcoeffsA[0] += p->buf[YADAPTCOEFFSA]; + p->YcoeffsA[1] += p->buf[YADAPTCOEFFSA-1]; + p->YcoeffsA[2] += p->buf[YADAPTCOEFFSA-2]; + p->YcoeffsA[3] += p->buf[YADAPTCOEFFSA-3]; + + p->YcoeffsB[0] += p->buf[YADAPTCOEFFSB]; + p->YcoeffsB[1] += p->buf[YADAPTCOEFFSB-1]; + p->YcoeffsB[2] += p->buf[YADAPTCOEFFSB-2]; + p->YcoeffsB[3] += p->buf[YADAPTCOEFFSB-3]; + p->YcoeffsB[4] += p->buf[YADAPTCOEFFSB-4]; + } + } + + *(decoded0++) = p->YfilterA; + + if (LIKELY(*decoded1 != 0)) + { + if (*decoded1 > 0) + { + p->XcoeffsA[0] -= p->buf[XADAPTCOEFFSA]; + p->XcoeffsA[1] -= p->buf[XADAPTCOEFFSA-1]; + p->XcoeffsA[2] -= p->buf[XADAPTCOEFFSA-2]; + p->XcoeffsA[3] -= p->buf[XADAPTCOEFFSA-3]; + + p->XcoeffsB[0] -= p->buf[XADAPTCOEFFSB]; + p->XcoeffsB[1] -= p->buf[XADAPTCOEFFSB-1]; + p->XcoeffsB[2] -= p->buf[XADAPTCOEFFSB-2]; + p->XcoeffsB[3] -= p->buf[XADAPTCOEFFSB-3]; + p->XcoeffsB[4] -= p->buf[XADAPTCOEFFSB-4]; + } + else + { + p->XcoeffsA[0] += p->buf[XADAPTCOEFFSA]; + p->XcoeffsA[1] += p->buf[XADAPTCOEFFSA-1]; + p->XcoeffsA[2] += p->buf[XADAPTCOEFFSA-2]; + p->XcoeffsA[3] += p->buf[XADAPTCOEFFSA-3]; + + p->XcoeffsB[0] += p->buf[XADAPTCOEFFSB]; + p->XcoeffsB[1] += p->buf[XADAPTCOEFFSB-1]; + p->XcoeffsB[2] += p->buf[XADAPTCOEFFSB-2]; + p->XcoeffsB[3] += p->buf[XADAPTCOEFFSB-3]; + p->XcoeffsB[4] += p->buf[XADAPTCOEFFSB-4]; + } + } + + *(decoded1++) = p->XfilterA; + + /* Combined */ + p->buf++; + + /* Have we filled the history buffer? */ + if (UNLIKELY(p->buf == p->historybuffer + PREDICTOR_HISTORY_SIZE)) { + memmove(p->historybuffer, p->buf, + PREDICTOR_SIZE * sizeof(int32_t)); + p->buf = p->historybuffer; + } + } +} + +void ICODE_ATTR_DEMAC predictor_decode_mono(struct predictor_t* p, + int32_t* decoded0, + int count) +{ + int32_t predictionA, currentA, A; + + currentA = p->YlastA; + + while (LIKELY(count--)) + { + A = *decoded0; + + p->buf[YDELAYA] = currentA; + p->buf[YDELAYA-1] = p->buf[YDELAYA] - p->buf[YDELAYA-1]; + + predictionA = (p->buf[YDELAYA] * p->YcoeffsA[0]) + + (p->buf[YDELAYA-1] * p->YcoeffsA[1]) + + (p->buf[YDELAYA-2] * p->YcoeffsA[2]) + + (p->buf[YDELAYA-3] * p->YcoeffsA[3]); + + currentA = A + (predictionA >> 10); + + p->buf[YADAPTCOEFFSA] = SIGN(p->buf[YDELAYA]); + p->buf[YADAPTCOEFFSA-1] = SIGN(p->buf[YDELAYA-1]); + + if (LIKELY(A != 0)) + { + if (A > 0) + { + p->YcoeffsA[0] -= p->buf[YADAPTCOEFFSA]; + p->YcoeffsA[1] -= p->buf[YADAPTCOEFFSA-1]; + p->YcoeffsA[2] -= p->buf[YADAPTCOEFFSA-2]; + p->YcoeffsA[3] -= p->buf[YADAPTCOEFFSA-3]; + } + else + { + p->YcoeffsA[0] += p->buf[YADAPTCOEFFSA]; + p->YcoeffsA[1] += p->buf[YADAPTCOEFFSA-1]; + p->YcoeffsA[2] += p->buf[YADAPTCOEFFSA-2]; + p->YcoeffsA[3] += p->buf[YADAPTCOEFFSA-3]; + } + } + + p->buf++; + + /* Have we filled the history buffer? */ + if (UNLIKELY(p->buf == p->historybuffer + PREDICTOR_HISTORY_SIZE)) { + memmove(p->historybuffer, p->buf, + PREDICTOR_SIZE * sizeof(int32_t)); + p->buf = p->historybuffer; + } + + p->YfilterA = currentA + ((p->YfilterA * 31) >> 5); + *(decoded0++) = p->YfilterA; + } + + p->YlastA = currentA; +} +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/predictor.h b/lib/rbcodec/codecs/demac/libdemac/predictor.h new file mode 100644 index 0000000000..6a0a81983b --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/predictor.h @@ -0,0 +1,38 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_PREDICTOR_H +#define _APE_PREDICTOR_H + +#include <inttypes.h> +#include "parser.h" +#include "filter.h" + +void init_predictor_decoder(struct predictor_t* p); +void predictor_decode_stereo(struct predictor_t* p, int32_t* decoded0, + int32_t* decoded1, int count); +void predictor_decode_mono(struct predictor_t* p, int32_t* decoded0, + int count); + +#endif diff --git a/lib/rbcodec/codecs/demac/libdemac/udiv32_arm-pre.S b/lib/rbcodec/codecs/demac/libdemac/udiv32_arm-pre.S new file mode 100644 index 0000000000..459cab8240 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/udiv32_arm-pre.S @@ -0,0 +1,25 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Andrew Mahone + * + * Wrapper for udiv32_arm.S to test available IRAM by pre-linking the codec. + * + * 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. + * + ****************************************************************************/ + +#define APE_PRE +#include "udiv32_arm.S" diff --git a/lib/rbcodec/codecs/demac/libdemac/udiv32_arm.S b/lib/rbcodec/codecs/demac/libdemac/udiv32_arm.S new file mode 100644 index 0000000000..7b851659bd --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/udiv32_arm.S @@ -0,0 +1,318 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Jens Arnold + * Copyright (C) 2009 by Andrew Mahone + * + * Optimised unsigned integer division for ARMv4 + * + * Based on: libgcc routines for ARM cpu, additional algorithms from ARM System + * Developer's Guide + * Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) + * Copyright 1995, 1996, 1998, 1999, 2000, 2003, 2004, 2005 + * Free Software Foundation, Inc. + * + * 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 "config.h" +/* On targets with codec iram, a header file will be generated after an initial + link of the APE codec, stating the amount of IRAM remaining for use by the + reciprocal lookup table. */ +#if !defined(APE_PRE) && defined(USE_IRAM) && ARM_ARCH < 5 +#include "lib/rbcodec/codecs/ape_free_iram.h" +#endif + +/* Codecs should not normally do this, but we need to check a macro, and + * codecs.h would confuse the assembler. */ + +#ifdef USE_IRAM +#define DIV_RECIP + .section .icode,"ax",%progbits +#else + .text +#endif + .align + .global udiv32_arm + .type udiv32_arm,%function + +#if ARM_ARCH < 5 +/* Adapted from an algorithm given in ARM System Developer's Guide (7.3.1.2) + for dividing a 30-bit value by a 15-bit value, with two operations per + iteration by storing quotient and remainder together and adding the previous + quotient bit during trial subtraction. Modified to work with any dividend + and divisor both less than 1 << 30, and skipping trials by calculating bits + in output. */ +.macro ARM_DIV_31_BODY dividend, divisor, result, bits, curbit, quotient, remainder + + mov \bits, #1 + /* Shift the divisor left until it aligns with the numerator. If it already + has the high bit set, this is fine, everything inside .rept will be + skipped, and the add before and adcs after will set the one-bit result + to zero. */ + cmn \divisor, \dividend, lsr #16 + movcs \divisor, \divisor, lsl #16 + addcs \bits, \bits, #16 + cmn \divisor, \dividend, lsr #8 + movcs \divisor, \divisor, lsl #8 + addcs \bits, \bits, #8 + cmn \divisor, \dividend, lsr #4 + movcs \divisor, \divisor, lsl #4 + addcs \bits, \bits, #4 + cmn \divisor, \dividend, lsr #2 + movcs \divisor, \divisor, lsl #2 + addcs \bits, \bits, #2 + cmn \divisor, \dividend, lsr #1 + movcs \divisor, \divisor, lsl #1 + addcs \bits, \bits, #1 + adds \result, \dividend, \divisor + subcc \result, \result, \divisor + rsb \curbit, \bits, #31 + add pc, pc, \curbit, lsl #3 + nop + .rept 30 + adcs \result, \divisor, \result, lsl #1 + /* Fix the remainder portion of the result. This must be done because the + handler for 32-bit numerators needs the remainder. */ + subcc \result, \result, \divisor + .endr + /* Shift remainder/quotient left one, add final quotient bit */ + adc \result, \result, \result + mov \remainder, \result, lsr \bits + eor \quotient, \result, \remainder, lsl \bits +.endm + +#ifndef FREE_IRAM +.set recip_max, 2 +#else +/* Each table entry is one word. Since a compare is done against the maximum + entry as an immediate, the maximum entry must be a valid ARM immediate, + which means a byte shifted by an even number of places. */ +.set recip_max, 2 + FREE_IRAM / 4 +.set recip_max_tmp, recip_max >> 8 +.set recip_mask_shift, 0 +.set tmp_shift, 16 +.rept 5 + .if recip_max_tmp >> tmp_shift + .set recip_max_tmp, recip_max_tmp >> tmp_shift + .set recip_mask_shift, recip_mask_shift + tmp_shift + .endif + .set tmp_shift, tmp_shift >> 1 +.endr +.if recip_max_tmp + .set recip_mask_shift, recip_mask_shift + 1 +.endif +.set recip_mask_shift, (recip_mask_shift + 1) & 62 +.set recip_max, recip_max & (255 << recip_mask_shift) +//.set recip_max, 2 +#endif + +udiv32_arm: +#ifdef DIV_RECIP + cmp r1, #3 + bcc .L_udiv_tiny + cmp r1, #recip_max + bhi .L_udiv + adr r3, .L_udiv_recip_table-12 + ldr r2, [r3, r1, lsl #2] + mov r3, r0 + umull ip, r0, r2, r0 + mul r2, r0, r1 + cmp r3, r2 + bxcs lr + sub r0, r0, #1 + bx lr +.L_udiv_tiny: + cmp r1, #1 + movhi r0, r0, lsr #1 + bxcs lr + b .L_div0 +#endif +.L_udiv: + /* Invert divisor. ARM_DIV_31_BODY uses adc to both subtract the divisor + and add the next bit of the result. The correction code at .L_udiv32 + does not need the divisor inverted, but can be modified to work with it, + and this allows the zero divisor test to be done early and without an + explicit comparison. */ + rsbs r1, r1, #0 +#ifndef DIV_RECIP + beq .L_div0 +#endif + tst r0, r0 + /* High bit must be unset, otherwise shift numerator right, calculate, + and correct results. As this case is very uncommon we want to avoid + any other delays on the main path in handling it, so the long divide + calls the short divide as a function. */ + bmi .L_udiv32 +.L_udiv31: + ARM_DIV_31_BODY r0, r1, r2, r3, ip, r0, r1 + bx lr +.L_udiv32: + /* store original numerator and divisor, we'll need them to correct the + result, */ + stmdb sp, { r0, r1, lr } + /* Call __div0 here if divisor is zero, otherwise it would report the wrong + address. */ + mov r0, r0, lsr #1 + bl .L_udiv31 + ldmdb sp, { r2, r3, lr } + /* Move the low bit of the original numerator to the carry bit */ + movs r2, r2, lsr #1 + /* Shift the remainder left one and add in the carry bit */ + adc r1, r1, r1 + /* Subtract the original divisor from the remainder, setting carry if the + result is non-negative */ + adds r1, r1, r3 + /* Shift quotient left one and add carry bit */ + adc r0, r0, r0 + bx lr +.L_div0: + /* __div0 expects the calling address on the top of the stack */ + stmdb sp!, { lr } + mov r0, #0 +#if defined(__ARM_EABI__) || !defined(USE_IRAM) + bl __div0 +#else + ldr pc, [pc, #-4] + .word __div0 +#endif +#ifdef DIV_RECIP +.L_udiv_recip_table: + .set div, 3 + .rept recip_max - 2 + .if (div - 1) & div + .set q, 0x40000000 / div + .set r, (0x40000000 - (q * div))<<1 + .set q, q << 1 + .if r >= div + .set q, q + 1 + .set r, r - div + .endif + .set r, r << 1 + .set q, q << 1 + .if r >= div + .set q, q + 1 + .set r, r - div + .endif + .set q, q + 1 + .else + .set q, 0x40000000 / div * 4 + .endif + .word q + .set div, div+1 + .endr +#endif + .size udiv32_arm, . - udiv32_arm + +#else +.macro ARMV5_UDIV32_BODY numerator, divisor, quotient, bits, inv, neg, div0label + cmp \numerator, \divisor + clz \bits, \divisor + bcc 30f + mov \inv, \divisor, lsl \bits + add \neg, pc, \inv, lsr #25 + cmp \inv, #1<<31 + ldrhib \inv, [\neg, #.L_udiv_est_table-.-64] + bls 20f + subs \bits, \bits, #7 + rsb \neg, \divisor, #0 + movpl \divisor, \inv, lsl \bits + bmi 10f + mul \inv, \divisor, \neg + smlawt \divisor, \divisor, \inv, \divisor + mul \inv, \divisor, \neg + /* This will save a cycle on ARMv6, but requires that the numerator sign + bit is not set (that of inv is guaranteed unset). The branch should + predict very well, making it typically 1 cycle, and thus both the branch + and test fill delay cycles for the multiplies. Based on logging of + numerator sizes in the APE codec, the branch is taken about 1/10^7 of + the time. */ +#if ARM_ARCH >= 6 + tst \numerator, \numerator + smmla \divisor, \divisor, \inv, \divisor + bmi 40f + smmul \inv, \numerator, \divisor +#else + mov \bits, #0 + smlal \bits, \divisor, \inv, \divisor + umull \bits, \inv, \numerator, \divisor +#endif + add \numerator, \numerator, \neg + mla \divisor, \inv, \neg, \numerator + mov \quotient, \inv + cmn \divisor, \neg + addcc \quotient, \quotient, #1 + addpl \quotient, \quotient, #2 + bx lr +10: + rsb \bits, \bits, #0 + sub \inv, \inv, #4 + mov \divisor, \inv, lsr \bits + umull \bits, \inv, \numerator, \divisor + mla \divisor, \inv, \neg, \numerator + mov \quotient, \inv + cmn \neg, \divisor, lsr #1 + addcs \divisor, \divisor, \neg, lsl #1 + addcs \quotient, \quotient, #2 + cmn \neg, \divisor + addcs \quotient, \quotient, #1 + bx lr +20: +.ifnc "", "\div0label" + rsb \bits, \bits, #31 + bne \div0label +.endif + mov \quotient, \numerator, lsr \bits + bx lr +30: + mov \quotient, #0 + bx lr +#if ARM_ARCH >= 6 +40: + umull \bits, \inv, \numerator, \divisor + add \numerator, \numerator, \neg + mla \divisor, \inv, \neg, \numerator + mov \quotient, \inv + cmn \divisor, \neg + addcc \quotient, \quotient, #1 + addpl \quotient, \quotient, #2 + bx lr +#endif +.endm + +udiv32_arm: + ARMV5_UDIV32_BODY r0, r1, r0, r2, r3, ip, .L_div0 +.L_div0: + /* __div0 expects the calling address on the top of the stack */ + stmdb sp!, { lr } + mov r0, #0 +#if defined(__ARM_EABI__) || !defined(USE_IRAM) + bl __div0 +#else + ldr pc, [pc, #-4] + .word __div0 +#endif +.L_udiv_est_table: + .byte 0xff, 0xfc, 0xf8, 0xf4, 0xf0, 0xed, 0xea, 0xe6 + .byte 0xe3, 0xe0, 0xdd, 0xda, 0xd7, 0xd4, 0xd2, 0xcf + .byte 0xcc, 0xca, 0xc7, 0xc5, 0xc3, 0xc0, 0xbe, 0xbc + .byte 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xb0, 0xae, 0xac + .byte 0xaa, 0xa8, 0xa7, 0xa5, 0xa3, 0xa2, 0xa0, 0x9f + .byte 0x9d, 0x9c, 0x9a, 0x99, 0x97, 0x96, 0x94, 0x93 + .byte 0x92, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8a, 0x89 + .byte 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81 +#endif + .size udiv32_arm, . - udiv32_arm diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv5te.h b/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv5te.h new file mode 100644 index 0000000000..ae7427c137 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv5te.h @@ -0,0 +1,404 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +ARMv5te vector math copyright (C) 2008 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define FUSED_VECTOR_MATH + +#define REPEAT_3(x) x x x +#if ORDER > 16 +#define REPEAT_MLA(x) x x x x x x x +#else +#define REPEAT_MLA(x) x x x +#endif + +/* Calculate scalarproduct, then add a 2nd vector (fused for performance) + * This version fetches data as 32 bit words, and *requires* v1 to be + * 32 bit aligned. It also requires that f2 and s2 are either both 32 bit + * aligned or both unaligned. If either condition isn't met, it will either + * result in a data abort or incorrect results. */ +static inline int32_t vector_sp_add(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 16 + int cnt = ORDER>>4; +#endif + +#define ADDHALFREGS(sum, s1, s2) /* Adds register */ \ + "mov " #s1 ", " #s1 ", ror #16 \n" /* halves straight */ \ + "add " #sum ", " #s1 ", " #s2 ", lsl #16 \n" /* Clobbers 's1' */ \ + "add " #s1 ", " #s1 ", " #s2 ", lsr #16 \n" \ + "mov " #s1 ", " #s1 ", lsl #16 \n" \ + "orr " #sum ", " #s1 ", " #sum ", lsr #16 \n" + +#define ADDHALFXREGS(sum, s1, s2) /* Adds register */ \ + "add " #s1 ", " #s1 ", " #sum ", lsl #16 \n" /* halves across. */ \ + "add " #sum ", " #s2 ", " #sum ", lsr #16 \n" /* Clobbers 's1'. */ \ + "mov " #sum ", " #sum ", lsl #16 \n" \ + "orr " #sum ", " #sum ", " #s1 ", lsr #16 \n" + + asm volatile ( +#if ORDER > 16 + "mov %[res], #0 \n" +#endif + "tst %[f2], #2 \n" + "beq 20f \n" + + "10: \n" + "ldrh r4, [%[s2]], #2 \n" + "mov r4, r4, lsl #16 \n" + "ldrh r3, [%[f2]], #2 \n" +#if ORDER > 16 + "mov r3, r3, lsl #16 \n" + "1: \n" + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" +#else + "ldmia %[v1], {r0,r1} \n" + "smulbb %[res], r0, r3 \n" +#endif + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r5} \n" + ADDHALFXREGS(r0, r4, r2) + ADDHALFXREGS(r1, r2, r5) + "stmia %[v1]!, {r0,r1} \n" + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r4} \n" + ADDHALFXREGS(r0, r5, r2) + ADDHALFXREGS(r1, r2, r4) + "stmia %[v1]!, {r0,r1} \n" + + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r5} \n" + ADDHALFXREGS(r0, r4, r2) + ADDHALFXREGS(r1, r2, r5) + "stmia %[v1]!, {r0,r1} \n" + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r4} \n" + ADDHALFXREGS(r0, r5, r2) + ADDHALFXREGS(r1, r2, r4) + "stmia %[v1]!, {r0,r1} \n" +#if ORDER > 16 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + "b 99f \n" + + "20: \n" + "1: \n" + "ldmia %[v1], {r1,r2} \n" + "ldmia %[f2]!, {r3,r4} \n" +#if ORDER > 16 + "smlabb %[res], r1, r3, %[res] \n" +#else + "smulbb %[res], r1, r3 \n" +#endif + "smlatt %[res], r1, r3, %[res] \n" + "smlabb %[res], r2, r4, %[res] \n" + "smlatt %[res], r2, r4, %[res] \n" + "ldmia %[s2]!, {r3,r4} \n" + ADDHALFREGS(r0, r1, r3) + ADDHALFREGS(r1, r2, r4) + "stmia %[v1]!, {r0,r1} \n" + + REPEAT_3( + "ldmia %[v1], {r1,r2} \n" + "ldmia %[f2]!, {r3,r4} \n" + "smlabb %[res], r1, r3, %[res] \n" + "smlatt %[res], r1, r3, %[res] \n" + "smlabb %[res], r2, r4, %[res] \n" + "smlatt %[res], r2, r4, %[res] \n" + "ldmia %[s2]!, {r3,r4} \n" + ADDHALFREGS(r0, r1, r3) + ADDHALFREGS(r1, r2, r4) + "stmia %[v1]!, {r0,r1} \n" + ) +#if ORDER > 16 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + + "99: \n" + : /* outputs */ +#if ORDER > 16 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", "r5", "cc", "memory" + ); + return res; +} + +/* Calculate scalarproduct, then subtract a 2nd vector (fused for performance) + * This version fetches data as 32 bit words, and *requires* v1 to be + * 32 bit aligned. It also requires that f2 and s2 are either both 32 bit + * aligned or both unaligned. If either condition isn't met, it will either + * result in a data abort or incorrect results. */ +static inline int32_t vector_sp_sub(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 16 + int cnt = ORDER>>4; +#endif + +#define SUBHALFREGS(dif, s1, s2) /* Subtracts reg. */ \ + "mov " #s1 ", " #s1 ", ror #16 \n" /* halves straight */ \ + "sub " #dif ", " #s1 ", " #s2 ", lsl #16 \n" /* Clobbers 's1' */ \ + "sub " #s1 ", " #s1 ", " #s2 ", lsr #16 \n" \ + "mov " #s1 ", " #s1 ", lsl #16 \n" \ + "orr " #dif ", " #s1 ", " #dif ", lsr #16 \n" + +#define SUBHALFXREGS(dif, s1, s2, msk) /* Subtracts reg. */ \ + "sub " #s1 ", " #dif ", " #s1 ", lsr #16 \n" /* halves across. */ \ + "and " #s1 ", " #s1 ", " #msk " \n" /* Needs msk = */ \ + "rsb " #dif ", " #s2 ", " #dif ", lsr #16 \n" /* 0x0000ffff, */ \ + "orr " #dif ", " #s1 ", " #dif ", lsl #16 \n" /* clobbers 's1'. */ + + asm volatile ( +#if ORDER > 16 + "mov %[res], #0 \n" +#endif + "tst %[f2], #2 \n" + "beq 20f \n" + + "10: \n" + "mov r6, #0xff \n" + "orr r6, r6, #0xff00 \n" + "ldrh r4, [%[s2]], #2 \n" + "mov r4, r4, lsl #16 \n" + "ldrh r3, [%[f2]], #2 \n" +#if ORDER > 16 + "mov r3, r3, lsl #16 \n" + "1: \n" + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" +#else + "ldmia %[v1], {r0,r1} \n" + "smulbb %[res], r0, r3 \n" +#endif + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r5} \n" + SUBHALFXREGS(r0, r4, r2, r6) + SUBHALFXREGS(r1, r2, r5, r6) + "stmia %[v1]!, {r0,r1} \n" + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r4} \n" + SUBHALFXREGS(r0, r5, r2, r6) + SUBHALFXREGS(r1, r2, r4, r6) + "stmia %[v1]!, {r0,r1} \n" + + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r5} \n" + SUBHALFXREGS(r0, r4, r2, r6) + SUBHALFXREGS(r1, r2, r5, r6) + "stmia %[v1]!, {r0,r1} \n" + "ldmia %[v1], {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + "ldmia %[s2]!, {r2,r4} \n" + SUBHALFXREGS(r0, r5, r2, r6) + SUBHALFXREGS(r1, r2, r4, r6) + "stmia %[v1]!, {r0,r1} \n" +#if ORDER > 16 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + "b 99f \n" + + "20: \n" + "1: \n" + "ldmia %[v1], {r1,r2} \n" + "ldmia %[f2]!, {r3,r4} \n" +#if ORDER > 16 + "smlabb %[res], r1, r3, %[res] \n" +#else + "smulbb %[res], r1, r3 \n" +#endif + "smlatt %[res], r1, r3, %[res] \n" + "smlabb %[res], r2, r4, %[res] \n" + "smlatt %[res], r2, r4, %[res] \n" + "ldmia %[s2]!, {r3,r4} \n" + SUBHALFREGS(r0, r1, r3) + SUBHALFREGS(r1, r2, r4) + "stmia %[v1]!, {r0,r1} \n" + + REPEAT_3( + "ldmia %[v1], {r1,r2} \n" + "ldmia %[f2]!, {r3,r4} \n" + "smlabb %[res], r1, r3, %[res] \n" + "smlatt %[res], r1, r3, %[res] \n" + "smlabb %[res], r2, r4, %[res] \n" + "smlatt %[res], r2, r4, %[res] \n" + "ldmia %[s2]!, {r3,r4} \n" + SUBHALFREGS(r0, r1, r3) + SUBHALFREGS(r1, r2, r4) + "stmia %[v1]!, {r0,r1} \n" + ) +#if ORDER > 16 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + + "99: \n" + : /* outputs */ +#if ORDER > 16 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "cc", "memory" + ); + return res; +} + +/* This version fetches data as 32 bit words, and *requires* v1 to be + * 32 bit aligned, otherwise it will result either in a data abort, or + * incorrect results (if ARM aligncheck is disabled). */ +static inline int32_t scalarproduct(int16_t* v1, int16_t* v2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" +#endif + "tst %[v2], #2 \n" + "beq 20f \n" + + "10: \n" + "ldrh r3, [%[v2]], #2 \n" +#if ORDER > 32 + "mov r3, r3, lsl #16 \n" + "1: \n" + "ldmia %[v1]!, {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" +#else + "ldmia %[v1]!, {r0,r1} \n" + "smulbb %[res], r0, r3 \n" +#endif + "ldmia %[v2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + + REPEAT_MLA( + "ldmia %[v1]!, {r0,r1} \n" + "smlabt %[res], r0, r3, %[res] \n" + "ldmia %[v2]!, {r2,r3} \n" + "smlatb %[res], r0, r2, %[res] \n" + "smlabt %[res], r1, r2, %[res] \n" + "smlatb %[res], r1, r3, %[res] \n" + ) +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + "b 99f \n" + + "20: \n" + "1: \n" + "ldmia %[v1]!, {r0,r1} \n" + "ldmia %[v2]!, {r2,r3} \n" +#if ORDER > 32 + "smlabb %[res], r0, r2, %[res] \n" +#else + "smulbb %[res], r0, r2 \n" +#endif + "smlatt %[res], r0, r2, %[res] \n" + "smlabb %[res], r1, r3, %[res] \n" + "smlatt %[res], r1, r3, %[res] \n" + + REPEAT_MLA( + "ldmia %[v1]!, {r0,r1} \n" + "ldmia %[v2]!, {r2,r3} \n" + "smlabb %[res], r0, r2, %[res] \n" + "smlatt %[res], r0, r2, %[res] \n" + "smlabb %[res], r1, r3, %[res] \n" + "smlatt %[res], r1, r3, %[res] \n" + ) +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + + "99: \n" + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [v2] "+r"(v2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "cc", "memory" + ); + return res; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv6.h b/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv6.h new file mode 100644 index 0000000000..8d27331b62 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv6.h @@ -0,0 +1,490 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +ARMv6 vector math copyright (C) 2008 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define FUSED_VECTOR_MATH + +#if ORDER > 16 +#define REPEAT_BLOCK(x) x x x +#else +#define REPEAT_BLOCK(x) x +#endif + +/* Calculate scalarproduct, then add a 2nd vector (fused for performance) + * This version fetches data as 32 bit words, and *requires* v1 to be + * 32 bit aligned. It also requires that f2 and s2 are either both 32 bit + * aligned or both unaligned. If either condition isn't met, it will either + * result in a data abort or incorrect results. */ +static inline int32_t vector_sp_add(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" +#endif + "tst %[f2], #2 \n" + "beq 20f \n" + + "10: \n" + "ldrh r3, [%[f2]], #2 \n" + "ldrh r6, [%[s2]], #2 \n" + "ldmia %[f2]!, {r2,r4} \n" + "mov r3, r3, lsl #16 \n" + "mov r6, r6, lsl #16 \n" + + "1: \n" + "ldmia %[s2]!, {r5,r7} \n" + "pkhtb r3, r3, r2 \n" + "pkhtb r2, r2, r4 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r6, r5, r6, asr #16 \n" + "pkhbt r5, r5, r7, lsl #16 \n" +#if ORDER > 32 + "smladx %[res], r0, r3, %[res] \n" +#else + "smuadx %[res], r0, r3 \n" +#endif + "smladx %[res], r1, r2, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "sadd16 r0, r0, r6 \n" + "sadd16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + + REPEAT_BLOCK( + "ldmia %[s2]!, {r5,r6} \n" + "pkhtb r4, r4, r2 \n" + "pkhtb r2, r2, r3 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r7, r5, r7, asr #16 \n" + "pkhbt r5, r5, r6, lsl #16 \n" + "smladx %[res], r0, r4, %[res] \n" + "smladx %[res], r1, r2, %[res] \n" + "ldmia %[f2]!, {r2,r4} \n" + "sadd16 r0, r0, r7 \n" + "sadd16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + "ldmia %[s2]!, {r5,r7} \n" + "pkhtb r3, r3, r2 \n" + "pkhtb r2, r2, r4 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r6, r5, r6, asr #16 \n" + "pkhbt r5, r5, r7, lsl #16 \n" + "smladx %[res], r0, r3, %[res] \n" + "smladx %[res], r1, r2, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "sadd16 r0, r0, r6 \n" + "sadd16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + ) + + "ldmia %[s2]!, {r5,r6} \n" + "pkhtb r4, r4, r2 \n" + "pkhtb r2, r2, r3 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r7, r5, r7, asr #16 \n" + "pkhbt r5, r5, r6, lsl #16 \n" + "smladx %[res], r0, r4, %[res] \n" + "smladx %[res], r1, r2, %[res] \n" +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "ldmneia %[f2]!, {r2,r4} \n" + "sadd16 r0, r0, r7 \n" + "sadd16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + "bne 1b \n" +#else + "sadd16 r0, r0, r7 \n" + "sadd16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" +#endif + + "b 99f \n" + + "20: \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r0, [%[v1]] \n" + +#if ORDER > 32 + "1: \n" + "smlad %[res], r0, r4, %[res] \n" +#else + "smuad %[res], r0, r4 \n" +#endif + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r1, r5, %[res] \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r2, [%[v1], #8] \n" + "sadd16 r0, r0, r6 \n" + "sadd16 r1, r1, r7 \n" + "strd r0, [%[v1]], #8 \n" + + REPEAT_BLOCK( + "smlad %[res], r2, r4, %[res] \n" + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r3, r5, %[res] \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r0, [%[v1], #8] \n" + "sadd16 r2, r2, r6 \n" + "sadd16 r3, r3, r7 \n" + "strd r2, [%[v1]], #8 \n" + "smlad %[res], r0, r4, %[res] \n" + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r1, r5, %[res] \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r2, [%[v1], #8] \n" + "sadd16 r0, r0, r6 \n" + "sadd16 r1, r1, r7 \n" + "strd r0, [%[v1]], #8 \n" + ) + + "smlad %[res], r2, r4, %[res] \n" + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r3, r5, %[res] \n" +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "ldrned r4, [%[f2]], #8 \n" + "ldrned r0, [%[v1], #8] \n" + "sadd16 r2, r2, r6 \n" + "sadd16 r3, r3, r7 \n" + "strd r2, [%[v1]], #8 \n" + "bne 1b \n" +#else + "sadd16 r2, r2, r6 \n" + "sadd16 r3, r3, r7 \n" + "strd r2, [%[v1]], #8 \n" +#endif + + "99: \n" + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", + "r5", "r6", "r7", "cc", "memory" + ); + return res; +} + +/* Calculate scalarproduct, then subtract a 2nd vector (fused for performance) + * This version fetches data as 32 bit words, and *requires* v1 to be + * 32 bit aligned. It also requires that f2 and s2 are either both 32 bit + * aligned or both unaligned. If either condition isn't met, it will either + * result in a data abort or incorrect results. */ +static inline int32_t vector_sp_sub(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" +#endif + "tst %[f2], #2 \n" + "beq 20f \n" + + "10: \n" + "ldrh r3, [%[f2]], #2 \n" + "ldrh r6, [%[s2]], #2 \n" + "ldmia %[f2]!, {r2,r4} \n" + "mov r3, r3, lsl #16 \n" + "mov r6, r6, lsl #16 \n" + + "1: \n" + "ldmia %[s2]!, {r5,r7} \n" + "pkhtb r3, r3, r2 \n" + "pkhtb r2, r2, r4 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r6, r5, r6, asr #16 \n" + "pkhbt r5, r5, r7, lsl #16 \n" +#if ORDER > 32 + "smladx %[res], r0, r3, %[res] \n" +#else + "smuadx %[res], r0, r3 \n" +#endif + "smladx %[res], r1, r2, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "ssub16 r0, r0, r6 \n" + "ssub16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + + REPEAT_BLOCK( + "ldmia %[s2]!, {r5,r6} \n" + "pkhtb r4, r4, r2 \n" + "pkhtb r2, r2, r3 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r7, r5, r7, asr #16 \n" + "pkhbt r5, r5, r6, lsl #16 \n" + "smladx %[res], r0, r4, %[res] \n" + "smladx %[res], r1, r2, %[res] \n" + "ldmia %[f2]!, {r2,r4} \n" + "ssub16 r0, r0, r7 \n" + "ssub16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + "ldmia %[s2]!, {r5,r7} \n" + "pkhtb r3, r3, r2 \n" + "pkhtb r2, r2, r4 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r6, r5, r6, asr #16 \n" + "pkhbt r5, r5, r7, lsl #16 \n" + "smladx %[res], r0, r3, %[res] \n" + "smladx %[res], r1, r2, %[res] \n" + "ldmia %[f2]!, {r2,r3} \n" + "ssub16 r0, r0, r6 \n" + "ssub16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + ) + + "ldmia %[s2]!, {r5,r6} \n" + "pkhtb r4, r4, r2 \n" + "pkhtb r2, r2, r3 \n" + "ldrd r0, [%[v1]] \n" + "mov r5, r5, ror #16 \n" + "pkhtb r7, r5, r7, asr #16 \n" + "pkhbt r5, r5, r6, lsl #16 \n" + "smladx %[res], r0, r4, %[res] \n" + "smladx %[res], r1, r2, %[res] \n" +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "ldmneia %[f2]!, {r2,r4} \n" + "ssub16 r0, r0, r7 \n" + "ssub16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" + "bne 1b \n" +#else + "ssub16 r0, r0, r7 \n" + "ssub16 r1, r1, r5 \n" + "strd r0, [%[v1]], #8 \n" +#endif + + "b 99f \n" + + "20: \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r0, [%[v1]] \n" + +#if ORDER > 32 + "1: \n" + "smlad %[res], r0, r4, %[res] \n" +#else + "smuad %[res], r0, r4 \n" +#endif + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r1, r5, %[res] \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r2, [%[v1], #8] \n" + "ssub16 r0, r0, r6 \n" + "ssub16 r1, r1, r7 \n" + "strd r0, [%[v1]], #8 \n" + + REPEAT_BLOCK( + "smlad %[res], r2, r4, %[res] \n" + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r3, r5, %[res] \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r0, [%[v1], #8] \n" + "ssub16 r2, r2, r6 \n" + "ssub16 r3, r3, r7 \n" + "strd r2, [%[v1]], #8 \n" + "smlad %[res], r0, r4, %[res] \n" + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r1, r5, %[res] \n" + "ldrd r4, [%[f2]], #8 \n" + "ldrd r2, [%[v1], #8] \n" + "ssub16 r0, r0, r6 \n" + "ssub16 r1, r1, r7 \n" + "strd r0, [%[v1]], #8 \n" + ) + + "smlad %[res], r2, r4, %[res] \n" + "ldrd r6, [%[s2]], #8 \n" + "smlad %[res], r3, r5, %[res] \n" +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "ldrned r4, [%[f2]], #8 \n" + "ldrned r0, [%[v1], #8] \n" + "ssub16 r2, r2, r6 \n" + "ssub16 r3, r3, r7 \n" + "strd r2, [%[v1]], #8 \n" + "bne 1b \n" +#else + "ssub16 r2, r2, r6 \n" + "ssub16 r3, r3, r7 \n" + "strd r2, [%[v1]], #8 \n" +#endif + + "99: \n" + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", + "r5", "r6", "r7", "cc", "memory" + ); + return res; +} + +/* This version fetches data as 32 bit words, and *requires* v1 to be + * 32 bit aligned, otherwise it will result either in a data abort, or + * incorrect results (if ARM aligncheck is disabled). */ +static inline int32_t scalarproduct(int16_t* v1, int16_t* v2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" +#endif + "tst %[v2], #2 \n" + "beq 20f \n" + + "10: \n" + "bic %[v2], %[v2], #2 \n" + "ldmia %[v2]!, {r5-r7} \n" + "ldrd r0, [%[v1]], #8 \n" + + "1: \n" + "pkhtb r3, r5, r6 \n" + "ldrd r4, [%[v2]], #8 \n" +#if ORDER > 32 + "smladx %[res], r0, r3, %[res] \n" +#else + "smuadx %[res], r0, r3 \n" +#endif + REPEAT_BLOCK( + "pkhtb r0, r6, r7 \n" + "ldrd r2, [%[v1]], #8 \n" + "smladx %[res], r1, r0, %[res] \n" + "pkhtb r1, r7, r4 \n" + "ldrd r6, [%[v2]], #8 \n" + "smladx %[res], r2, r1, %[res] \n" + "pkhtb r2, r4, r5 \n" + "ldrd r0, [%[v1]], #8 \n" + "smladx %[res], r3, r2, %[res] \n" + "pkhtb r3, r5, r6 \n" + "ldrd r4, [%[v2]], #8 \n" + "smladx %[res], r0, r3, %[res] \n" + ) + + "pkhtb r0, r6, r7 \n" + "ldrd r2, [%[v1]], #8 \n" + "smladx %[res], r1, r0, %[res] \n" + "pkhtb r1, r7, r4 \n" +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "ldrned r6, [%[v2]], #8 \n" + "smladx %[res], r2, r1, %[res] \n" + "pkhtb r2, r4, r5 \n" + "ldrned r0, [%[v1]], #8 \n" + "smladx %[res], r3, r2, %[res] \n" + "bne 1b \n" +#else + "pkhtb r4, r4, r5 \n" + "smladx %[res], r2, r1, %[res] \n" + "smladx %[res], r3, r4, %[res] \n" +#endif + + "b 99f \n" + + "20: \n" + "ldrd r0, [%[v1]], #8 \n" + "ldmia %[v2]!, {r5-r7} \n" + + "1: \n" + "ldrd r2, [%[v1]], #8 \n" +#if ORDER > 32 + "smlad %[res], r0, r5, %[res] \n" +#else + "smuad %[res], r0, r5 \n" +#endif + REPEAT_BLOCK( + "ldrd r4, [%[v2]], #8 \n" + "smlad %[res], r1, r6, %[res] \n" + "ldrd r0, [%[v1]], #8 \n" + "smlad %[res], r2, r7, %[res] \n" + "ldrd r6, [%[v2]], #8 \n" + "smlad %[res], r3, r4, %[res] \n" + "ldrd r2, [%[v1]], #8 \n" + "smlad %[res], r0, r5, %[res] \n" + ) + +#if ORDER > 32 + "ldrd r4, [%[v2]], #8 \n" + "smlad %[res], r1, r6, %[res] \n" + "subs %[cnt], %[cnt], #1 \n" + "ldrned r0, [%[v1]], #8 \n" + "smlad %[res], r2, r7, %[res] \n" + "ldrned r6, [%[v2]], #8 \n" + "smlad %[res], r3, r4, %[res] \n" + "bne 1b \n" +#else + "ldr r4, [%[v2]], #4 \n" + "smlad %[res], r1, r6, %[res] \n" + "smlad %[res], r2, r7, %[res] \n" + "smlad %[res], r3, r4, %[res] \n" +#endif + + "99: \n" + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [v2] "+r"(v2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", + "r4", "r5", "r6", "r7", "cc", "memory" + ); + return res; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv7.h b/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv7.h new file mode 100644 index 0000000000..84afda3e5d --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math16_armv7.h @@ -0,0 +1,214 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +ARMv7 neon vector math copyright (C) 2010 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define FUSED_VECTOR_MATH + +#if ORDER > 32 +#define REPEAT_BLOCK(x) x x x +#elif ORDER > 16 +#define REPEAT_BLOCK(x) x +#else +#define REPEAT_BLOCK(x) +#endif + +/* Calculate scalarproduct, then add a 2nd vector (fused for performance) */ +static inline int32_t vector_sp_add(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 64 + int cnt = ORDER>>6; +#endif + + asm volatile ( +#if ORDER > 64 + "vmov.i16 q0, #0 \n" + "1: \n" + "subs %[cnt], %[cnt], #1 \n" +#endif + "vld1.16 {d6-d9}, [%[f2]]! \n" + "vld1.16 {d2-d5}, [%[v1]] \n" + "vld1.16 {d10-d13}, [%[s2]]! \n" +#if ORDER > 64 + "vmlal.s16 q0, d2, d6 \n" +#else + "vmull.s16 q0, d2, d6 \n" +#endif + "vmlal.s16 q0, d3, d7 \n" + "vmlal.s16 q0, d4, d8 \n" + "vmlal.s16 q0, d5, d9 \n" + "vadd.i16 q1, q1, q5 \n" + "vadd.i16 q2, q2, q6 \n" + "vst1.16 {d2-d5}, [%[v1]]! \n" + + REPEAT_BLOCK( + "vld1.16 {d6-d9}, [%[f2]]! \n" + "vld1.16 {d2-d5}, [%[v1]] \n" + "vld1.16 {d10-d13}, [%[s2]]! \n" + "vmlal.s16 q0, d2, d6 \n" + "vmlal.s16 q0, d3, d7 \n" + "vmlal.s16 q0, d4, d8 \n" + "vmlal.s16 q0, d5, d9 \n" + "vadd.i16 q1, q1, q5 \n" + "vadd.i16 q2, q2, q6 \n" + "vst1.16 {d2-d5}, [%[v1]]! \n" + ) +#if ORDER > 64 + "bne 1b \n" +#endif + "vpadd.i32 d0, d0, d1 \n" + "vpaddl.s32 d0, d0 \n" + "vmov.32 %[res], d0[0] \n" + : /* outputs */ +#if ORDER > 64 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "memory" + ); + return res; +} + +/* Calculate scalarproduct, then subtract a 2nd vector (fused for performance) */ +static inline int32_t vector_sp_sub(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 64 + int cnt = ORDER>>6; +#endif + + asm volatile ( +#if ORDER > 64 + "vmov.i16 q0, #0 \n" + "1: \n" + "subs %[cnt], %[cnt], #1 \n" +#endif + "vld1.16 {d6-d9}, [%[f2]]! \n" + "vld1.16 {d2-d5}, [%[v1]] \n" + "vld1.16 {d10-d13}, [%[s2]]! \n" +#if ORDER > 64 + "vmlal.s16 q0, d2, d6 \n" +#else + "vmull.s16 q0, d2, d6 \n" +#endif + "vmlal.s16 q0, d3, d7 \n" + "vmlal.s16 q0, d4, d8 \n" + "vmlal.s16 q0, d5, d9 \n" + "vsub.i16 q1, q1, q5 \n" + "vsub.i16 q2, q2, q6 \n" + "vst1.16 {d2-d5}, [%[v1]]! \n" + + REPEAT_BLOCK( + "vld1.16 {d6-d9}, [%[f2]]! \n" + "vld1.16 {d2-d5}, [%[v1]] \n" + "vld1.16 {d10-d13}, [%[s2]]! \n" + "vmlal.s16 q0, d2, d6 \n" + "vmlal.s16 q0, d3, d7 \n" + "vmlal.s16 q0, d4, d8 \n" + "vmlal.s16 q0, d5, d9 \n" + "vsub.i16 q1, q1, q5 \n" + "vsub.i16 q2, q2, q6 \n" + "vst1.16 {d2-d5}, [%[v1]]! \n" + ) +#if ORDER > 64 + "bne 1b \n" +#endif + "vpadd.i32 d0, d0, d1 \n" + "vpaddl.s32 d0, d0 \n" + "vmov.32 %[res], d0[0] \n" + : /* outputs */ +#if ORDER > 64 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "memory" + ); + return res; +} + +static inline int32_t scalarproduct(int16_t* v1, int16_t* v2) +{ + int res; +#if ORDER > 64 + int cnt = ORDER>>6; +#endif + + asm volatile ( +#if ORDER > 64 + "vmov.i16 q0, #0 \n" + "1: \n" + "subs %[cnt], %[cnt], #1 \n" +#endif + "vld1.16 {d2-d5}, [%[v1]]! \n" + "vld1.16 {d6-d9}, [%[v2]]! \n" +#if ORDER > 64 + "vmlal.s16 q0, d2, d6 \n" +#else + "vmull.s16 q0, d2, d6 \n" +#endif + "vmlal.s16 q0, d3, d7 \n" + "vmlal.s16 q0, d4, d8 \n" + "vmlal.s16 q0, d5, d9 \n" + + REPEAT_BLOCK( + "vld1.16 {d2-d5}, [%[v1]]! \n" + "vld1.16 {d6-d9}, [%[v2]]! \n" + "vmlal.s16 q0, d2, d6 \n" + "vmlal.s16 q0, d3, d7 \n" + "vmlal.s16 q0, d4, d8 \n" + "vmlal.s16 q0, d5, d9 \n" + ) +#if ORDER > 64 + "bne 1b \n" +#endif + "vpadd.i32 d0, d0, d1 \n" + "vpaddl.s32 d0, d0 \n" + "vmov.32 %[res], d0[0] \n" + : /* outputs */ +#if ORDER > 64 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [v2] "+r"(v2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "d0", "d1", "d2", "d3", "d4", + "d5", "d6", "d7", "d8", "d9" + ); + return res; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math16_cf.h b/lib/rbcodec/codecs/demac/libdemac/vector_math16_cf.h new file mode 100644 index 0000000000..4d77d3be31 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math16_cf.h @@ -0,0 +1,364 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +Coldfire vector math copyright (C) 2007 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define FUSED_VECTOR_MATH + +#define PREPARE_SCALARPRODUCT coldfire_set_macsr(0); /* signed integer mode */ + +#define REPEAT_2(x) x x +#define REPEAT_3(x) x x x +#define REPEAT_7(x) x x x x x x x + +/* Calculate scalarproduct, then add a 2nd vector (fused for performance) + * This version fetches data as 32 bit words, and *recommends* v1 to be + * 32 bit aligned. It also assumes that f2 and s2 are either both 32 bit + * aligned or both unaligned. Performance will suffer if either condition + * isn't met. It also needs EMAC in signed integer mode. */ +static inline int32_t vector_sp_add(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 16 + int cnt = ORDER>>4; +#endif + +#define ADDHALFREGS(s1, s2, sum) /* Add register halves straight. */ \ + "move.l " #s1 ", " #sum "\n" /* 's1' and 's2' can be A or D */ \ + "add.l " #s2 ", " #s1 "\n" /* regs, 'sum' must be a D reg. */ \ + "clr.w " #sum " \n" /* 's1' is clobbered! */ \ + "add.l " #s2 ", " #sum "\n" \ + "move.w " #s1 ", " #sum "\n" + +#define ADDHALFXREGS(s1, s2, sum) /* Add register halves across. */ \ + "clr.w " #sum " \n" /* Needs 'sum' pre-swapped, swaps */ \ + "add.l " #s1 ", " #sum "\n" /* 's2', and clobbers 's1'. */ \ + "swap " #s2 " \n" /* 's1' can be an A or D reg. */ \ + "add.l " #s2 ", " #s1 "\n" /* 'sum' and 's2' must be D regs. */ \ + "move.w " #s1 ", " #sum "\n" + + asm volatile ( + "move.l %[f2], %%d0 \n" + "and.l #2, %%d0 \n" + "jeq 20f \n" + + "10: \n" + "move.w (%[f2])+, %%d0 \n" + "move.w (%[s2])+, %%d1 \n" + "swap %%d1 \n" + "1: \n" + REPEAT_2( + "movem.l (%[v1]), %%d6-%%d7/%%a0-%%a1 \n" + "mac.w %%d0l, %%d6u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%d6l, (%[s2])+, %%d2, %%acc0\n" + ADDHALFXREGS(%%d6, %%d2, %%d1) + "mac.w %%d0l, %%d7u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%d7l, (%[s2])+, %%d6, %%acc0\n" + "move.l %%d1, (%[v1])+ \n" + ADDHALFXREGS(%%d7, %%d6, %%d2) + "mac.w %%d0l, %%a0u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%a0l, (%[s2])+, %%d7, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + ADDHALFXREGS(%%a0, %%d7, %%d6) + "mac.w %%d0l, %%a1u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%a1l, (%[s2])+, %%d1, %%acc0\n" + "move.l %%d6, (%[v1])+ \n" + ADDHALFXREGS(%%a1, %%d1, %%d7) + "move.l %%d7, (%[v1])+ \n" + ) + +#if ORDER > 16 + "subq.l #1, %[res] \n" + "bne.w 1b \n" +#endif + "jra 99f \n" + + "20: \n" + "move.l (%[f2])+, %%d0 \n" + "1: \n" + "movem.l (%[v1]), %%d6-%%d7/%%a0-%%a1 \n" + "mac.w %%d0u, %%d6u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d6l, (%[f2])+, %%d0, %%acc0\n" + ADDHALFREGS(%%d6, %%d1, %%d2) + "mac.w %%d0u, %%d7u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d7l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + ADDHALFREGS(%%d7, %%d1, %%d2) + "mac.w %%d0u, %%a0u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%a0l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + ADDHALFREGS(%%a0, %%d1, %%d2) + "mac.w %%d0u, %%a1u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%a1l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + ADDHALFREGS(%%a1, %%d1, %%d2) + "move.l %%d2, (%[v1])+ \n" + + "movem.l (%[v1]), %%d6-%%d7/%%a0-%%a1 \n" + "mac.w %%d0u, %%d6u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d6l, (%[f2])+, %%d0, %%acc0\n" + ADDHALFREGS(%%d6, %%d1, %%d2) + "mac.w %%d0u, %%d7u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d7l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + ADDHALFREGS(%%d7, %%d1, %%d2) + "mac.w %%d0u, %%a0u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%a0l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + ADDHALFREGS(%%a0, %%d1, %%d2) + "mac.w %%d0u, %%a1u, (%[s2])+, %%d1, %%acc0\n" +#if ORDER > 16 + "mac.w %%d0l, %%a1l, (%[f2])+, %%d0, %%acc0\n" +#else + "mac.w %%d0l, %%a1l, %%acc0 \n" +#endif + "move.l %%d2, (%[v1])+ \n" + ADDHALFREGS(%%a1, %%d1, %%d2) + "move.l %%d2, (%[v1])+ \n" +#if ORDER > 16 + "subq.l #1, %[res] \n" + "bne.w 1b \n" +#endif + + "99: \n" + "movclr.l %%acc0, %[res] \n" + : /* outputs */ + [v1]"+a"(v1), + [f2]"+a"(f2), + [s2]"+a"(s2), + [res]"=d"(res) + : /* inputs */ +#if ORDER > 16 + [cnt]"[res]"(cnt) +#endif + : /* clobbers */ + "d0", "d1", "d2", "d6", "d7", + "a0", "a1", "memory" + + ); + return res; +} + +/* Calculate scalarproduct, then subtract a 2nd vector (fused for performance) + * This version fetches data as 32 bit words, and *recommends* v1 to be + * 32 bit aligned. It also assumes that f2 and s2 are either both 32 bit + * aligned or both unaligned. Performance will suffer if either condition + * isn't met. It also needs EMAC in signed integer mode. */ +static inline int32_t vector_sp_sub(int16_t* v1, int16_t* f2, int16_t* s2) +{ + int res; +#if ORDER > 16 + int cnt = ORDER>>4; +#endif + +#define SUBHALFREGS(min, sub, dif) /* Subtract register halves straight. */ \ + "move.l " #min ", " #dif "\n" /* 'min' can be an A or D reg */ \ + "sub.l " #sub ", " #min "\n" /* 'sub' and 'dif' must be D regs */ \ + "clr.w " #sub "\n" /* 'min' and 'sub' are clobbered! */ \ + "sub.l " #sub ", " #dif "\n" \ + "move.w " #min ", " #dif "\n" + +#define SUBHALFXREGS(min, s2, s1d) /* Subtract register halves across. */ \ + "clr.w " #s1d "\n" /* Needs 's1d' pre-swapped, swaps */ \ + "sub.l " #s1d ", " #min "\n" /* 's2' and clobbers 'min'. */ \ + "move.l " #min ", " #s1d "\n" /* 'min' can be an A or D reg, */ \ + "swap " #s2 "\n" /* 's2' and 's1d' must be D regs. */ \ + "sub.l " #s2 ", " #min "\n" \ + "move.w " #min ", " #s1d "\n" + + asm volatile ( + "move.l %[f2], %%d0 \n" + "and.l #2, %%d0 \n" + "jeq 20f \n" + + "10: \n" + "move.w (%[f2])+, %%d0 \n" + "move.w (%[s2])+, %%d1 \n" + "swap %%d1 \n" + "1: \n" + REPEAT_2( + "movem.l (%[v1]), %%d6-%%d7/%%a0-%%a1 \n" + "mac.w %%d0l, %%d6u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%d6l, (%[s2])+, %%d2, %%acc0\n" + SUBHALFXREGS(%%d6, %%d2, %%d1) + "mac.w %%d0l, %%d7u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%d7l, (%[s2])+, %%d6, %%acc0\n" + "move.l %%d1, (%[v1])+ \n" + SUBHALFXREGS(%%d7, %%d6, %%d2) + "mac.w %%d0l, %%a0u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%a0l, (%[s2])+, %%d7, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + SUBHALFXREGS(%%a0, %%d7, %%d6) + "mac.w %%d0l, %%a1u, (%[f2])+, %%d0, %%acc0\n" + "mac.w %%d0u, %%a1l, (%[s2])+, %%d1, %%acc0\n" + "move.l %%d6, (%[v1])+ \n" + SUBHALFXREGS(%%a1, %%d1, %%d7) + "move.l %%d7, (%[v1])+ \n" + ) + +#if ORDER > 16 + "subq.l #1, %[res] \n" + "bne.w 1b \n" +#endif + + "jra 99f \n" + + "20: \n" + "move.l (%[f2])+, %%d0 \n" + "1: \n" + "movem.l (%[v1]), %%d6-%%d7/%%a0-%%a1 \n" + "mac.w %%d0u, %%d6u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d6l, (%[f2])+, %%d0, %%acc0\n" + SUBHALFREGS(%%d6, %%d1, %%d2) + "mac.w %%d0u, %%d7u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d7l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + SUBHALFREGS(%%d7, %%d1, %%d2) + "mac.w %%d0u, %%a0u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%a0l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + SUBHALFREGS(%%a0, %%d1, %%d2) + "mac.w %%d0u, %%a1u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%a1l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + SUBHALFREGS(%%a1, %%d1, %%d2) + "move.l %%d2, (%[v1])+ \n" + + "movem.l (%[v1]), %%d6-%%d7/%%a0-%%a1 \n" + "mac.w %%d0u, %%d6u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d6l, (%[f2])+, %%d0, %%acc0\n" + SUBHALFREGS(%%d6, %%d1, %%d2) + "mac.w %%d0u, %%d7u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d7l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + SUBHALFREGS(%%d7, %%d1, %%d2) + "mac.w %%d0u, %%a0u, (%[s2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%a0l, (%[f2])+, %%d0, %%acc0\n" + "move.l %%d2, (%[v1])+ \n" + SUBHALFREGS(%%a0, %%d1, %%d2) + "mac.w %%d0u, %%a1u, (%[s2])+, %%d1, %%acc0\n" +#if ORDER > 16 + "mac.w %%d0l, %%a1l, (%[f2])+, %%d0, %%acc0\n" +#else + "mac.w %%d0l, %%a1l, %%acc0 \n" +#endif + "move.l %%d2, (%[v1])+ \n" + SUBHALFREGS(%%a1, %%d1, %%d2) + "move.l %%d2, (%[v1])+ \n" +#if ORDER > 16 + "subq.l #1, %[res] \n" + "bne.w 1b \n" +#endif + + "99: \n" + "movclr.l %%acc0, %[res] \n" + : /* outputs */ + [v1]"+a"(v1), + [f2]"+a"(f2), + [s2]"+a"(s2), + [res]"=d"(res) + : /* inputs */ +#if ORDER > 16 + [cnt]"[res]"(cnt) +#endif + : /* clobbers */ + "d0", "d1", "d2", "d6", "d7", + "a0", "a1", "memory" + + ); + return res; +} + +/* This version fetches data as 32 bit words, and *recommends* v1 to be + * 32 bit aligned, otherwise performance will suffer. It also needs EMAC + * in signed integer mode. */ +static inline int32_t scalarproduct(int16_t* v1, int16_t* v2) +{ + int res; +#if ORDER > 16 + int cnt = ORDER>>4; +#endif + + asm volatile ( + "move.l %[v2], %%d0 \n" + "and.l #2, %%d0 \n" + "jeq 20f \n" + + "10: \n" + "move.l (%[v1])+, %%d0 \n" + "move.w (%[v2])+, %%d1 \n" + "1: \n" + REPEAT_7( + "mac.w %%d0u, %%d1l, (%[v2])+, %%d1, %%acc0\n" + "mac.w %%d0l, %%d1u, (%[v1])+, %%d0, %%acc0\n" + ) + + "mac.w %%d0u, %%d1l, (%[v2])+, %%d1, %%acc0\n" +#if ORDER > 16 + "mac.w %%d0l, %%d1u, (%[v1])+, %%d0, %%acc0\n" + "subq.l #1, %[res] \n" + "bne.b 1b \n" +#else + "mac.w %%d0l, %%d1u, %%acc0 \n" +#endif + "jra 99f \n" + + "20: \n" + "move.l (%[v1])+, %%d0 \n" + "move.l (%[v2])+, %%d1 \n" + "1: \n" + REPEAT_3( + "mac.w %%d0u, %%d1u, (%[v1])+, %%d2, %%acc0\n" + "mac.w %%d0l, %%d1l, (%[v2])+, %%d1, %%acc0\n" + "mac.w %%d2u, %%d1u, (%[v1])+, %%d0, %%acc0\n" + "mac.w %%d2l, %%d1l, (%[v2])+, %%d1, %%acc0\n" + ) + + "mac.w %%d0u, %%d1u, (%[v1])+, %%d2, %%acc0\n" + "mac.w %%d0l, %%d1l, (%[v2])+, %%d1, %%acc0\n" +#if ORDER > 16 + "mac.w %%d2u, %%d1u, (%[v1])+, %%d0, %%acc0\n" + "mac.w %%d2l, %%d1l, (%[v2])+, %%d1, %%acc0\n" + "subq.l #1, %[res] \n" + "bne.b 1b \n" +#else + "mac.w %%d2u, %%d1u, %%acc0 \n" + "mac.w %%d2l, %%d1l, %%acc0 \n" +#endif + + "99: \n" + "movclr.l %%acc0, %[res] \n" + : /* outputs */ + [v1]"+a"(v1), + [v2]"+a"(v2), + [res]"=d"(res) + : /* inputs */ +#if ORDER > 16 + [cnt]"[res]"(cnt) +#endif + : /* clobbers */ + "d0", "d1", "d2" + ); + return res; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math16_mmx.h b/lib/rbcodec/codecs/demac/libdemac/vector_math16_mmx.h new file mode 100644 index 0000000000..2177fe88ea --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math16_mmx.h @@ -0,0 +1,234 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +MMX vector math copyright (C) 2010 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define FUSED_VECTOR_MATH + +#define REPEAT_MB3(x, n) x(n) x(n+8) x(n+16) +#define REPEAT_MB7(x, n) x(n) x(n+8) x(n+16) x(n+24) x(n+32) x(n+40) x(n+48) +#define REPEAT_MB8(x, n) REPEAT_MB7(x, n) x(n+56) + +#if ORDER == 16 /* 3 times */ +#define REPEAT_MB(x) REPEAT_MB3(x, 8) +#elif ORDER == 32 /* 7 times */ +#define REPEAT_MB(x) REPEAT_MB7(x, 8) +#elif ORDER == 64 /* 5*3 == 15 times */ +#define REPEAT_MB(x) REPEAT_MB3(x, 8) REPEAT_MB3(x, 32) REPEAT_MB3(x, 56) \ + REPEAT_MB3(x, 80) REPEAT_MB3(x, 104) +#elif ORDER == 256 /* 9*7 == 63 times */ +#define REPEAT_MB(x) REPEAT_MB7(x, 8) REPEAT_MB7(x, 64) REPEAT_MB7(x, 120) \ + REPEAT_MB7(x, 176) REPEAT_MB7(x, 232) REPEAT_MB7(x, 288) \ + REPEAT_MB7(x, 344) REPEAT_MB7(x, 400) REPEAT_MB7(x, 456) +#elif ORDER == 1280 /* 8*8 == 64 times */ +#define REPEAT_MB(x) REPEAT_MB8(x, 0) REPEAT_MB8(x, 64) REPEAT_MB8(x, 128) \ + REPEAT_MB8(x, 192) REPEAT_MB8(x, 256) REPEAT_MB8(x, 320) \ + REPEAT_MB8(x, 384) REPEAT_MB8(x, 448) +#else +#error unsupported order +#endif + + +static inline int32_t vector_sp_add(int16_t* v1, int16_t* f2, int16_t *s2) +{ + int res, t; +#if ORDER > 256 + int cnt = ORDER>>8; +#endif + + asm volatile ( +#if ORDER > 256 + "pxor %%mm2, %%mm2 \n" + "1: \n" +#else + "movq (%[v1]), %%mm2 \n" + "movq %%mm2, %%mm0 \n" + "pmaddwd (%[f2]), %%mm2 \n" + "paddw (%[s2]), %%mm0 \n" + "movq %%mm0, (%[v1]) \n" +#endif + +#define SP_ADD_BLOCK(n) \ + "movq " #n "(%[v1]), %%mm1 \n" \ + "movq %%mm1, %%mm0 \n" \ + "pmaddwd " #n "(%[f2]), %%mm1 \n" \ + "paddw " #n "(%[s2]), %%mm0 \n" \ + "movq %%mm0, " #n "(%[v1]) \n" \ + "paddd %%mm1, %%mm2 \n" + +REPEAT_MB(SP_ADD_BLOCK) + +#if ORDER > 256 + "add $512, %[v1] \n" + "add $512, %[s2] \n" + "add $512, %[f2] \n" + "dec %[cnt] \n" + "jne 1b \n" +#endif + + "movd %%mm2, %[t] \n" + "psrlq $32, %%mm2 \n" + "movd %%mm2, %[res] \n" + "add %[t], %[res] \n" + : /* outputs */ +#if ORDER > 256 + [cnt]"+r"(cnt), + [s2] "+r"(s2), + [res]"=r"(res), + [t] "=r"(t) + : /* inputs */ + [v1]"2"(v1), + [f2]"3"(f2) +#else + [res]"=r"(res), + [t] "=r"(t) + : /* inputs */ + [v1]"r"(v1), + [f2]"r"(f2), + [s2]"r"(s2) +#endif + : /* clobbers */ + "mm0", "mm1", "mm2" + ); + return res; +} + +static inline int32_t vector_sp_sub(int16_t* v1, int16_t* f2, int16_t *s2) +{ + int res, t; +#if ORDER > 256 + int cnt = ORDER>>8; +#endif + + asm volatile ( +#if ORDER > 256 + "pxor %%mm2, %%mm2 \n" + "1: \n" +#else + "movq (%[v1]), %%mm2 \n" + "movq %%mm2, %%mm0 \n" + "pmaddwd (%[f2]), %%mm2 \n" + "psubw (%[s2]), %%mm0 \n" + "movq %%mm0, (%[v1]) \n" +#endif + +#define SP_SUB_BLOCK(n) \ + "movq " #n "(%[v1]), %%mm1 \n" \ + "movq %%mm1, %%mm0 \n" \ + "pmaddwd " #n "(%[f2]), %%mm1 \n" \ + "psubw " #n "(%[s2]), %%mm0 \n" \ + "movq %%mm0, " #n "(%[v1]) \n" \ + "paddd %%mm1, %%mm2 \n" + +REPEAT_MB(SP_SUB_BLOCK) + +#if ORDER > 256 + "add $512, %[v1] \n" + "add $512, %[s2] \n" + "add $512, %[f2] \n" + "dec %[cnt] \n" + "jne 1b \n" +#endif + + "movd %%mm2, %[t] \n" + "psrlq $32, %%mm2 \n" + "movd %%mm2, %[res] \n" + "add %[t], %[res] \n" + : /* outputs */ +#if ORDER > 256 + [cnt]"+r"(cnt), + [s2] "+r"(s2), + [res]"=r"(res), + [t] "=r"(t) + : /* inputs */ + [v1]"2"(v1), + [f2]"3"(f2) +#else + [res]"=r"(res), + [t] "=r"(t) + : /* inputs */ + [v1]"r"(v1), + [f2]"r"(f2), + [s2]"r"(s2) +#endif + : /* clobbers */ + "mm0", "mm1", "mm2" + ); + return res; +} + +static inline int32_t scalarproduct(int16_t* v1, int16_t* v2) +{ + int res, t; +#if ORDER > 256 + int cnt = ORDER>>8; +#endif + + asm volatile ( +#if ORDER > 256 + "pxor %%mm1, %%mm1 \n" + "1: \n" +#else + "movq (%[v1]), %%mm1 \n" + "pmaddwd (%[v2]), %%mm1 \n" +#endif + +#define SP_BLOCK(n) \ + "movq " #n "(%[v1]), %%mm0 \n" \ + "pmaddwd " #n "(%[v2]), %%mm0 \n" \ + "paddd %%mm0, %%mm1 \n" + +REPEAT_MB(SP_BLOCK) + +#if ORDER > 256 + "add $512, %[v1] \n" + "add $512, %[v2] \n" + "dec %[cnt] \n" + "jne 1b \n" +#endif + + "movd %%mm1, %[t] \n" + "psrlq $32, %%mm1 \n" + "movd %%mm1, %[res] \n" + "add %[t], %[res] \n" + : /* outputs */ +#if ORDER > 256 + [cnt]"+r"(cnt), + [res]"=r"(res), + [t] "=r"(t) + : /* inputs */ + [v1]"1"(v1), + [v2]"2"(v2) +#else + [res]"=r"(res), + [t] "=r"(t) + : /* inputs */ + [v1]"r"(v1), + [v2]"r"(v2) +#endif + : /* clobbers */ + "mm0", "mm1" + ); + return res; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math32_armv4.h b/lib/rbcodec/codecs/demac/libdemac/vector_math32_armv4.h new file mode 100644 index 0000000000..d6bb9b0d9c --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math32_armv4.h @@ -0,0 +1,201 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +ARMv4 vector math copyright (C) 2008 Jens Arnold + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#define FUSED_VECTOR_MATH + +#if ORDER > 32 +#define REPEAT_BLOCK(x) x x x x x x x x +#elif ORDER > 16 +#define REPEAT_BLOCK(x) x x x x x x x +#else +#define REPEAT_BLOCK(x) x x x +#endif + +/* Calculate scalarproduct, then add a 2nd vector (fused for performance) */ +static inline int32_t vector_sp_add(int32_t* v1, int32_t* f2, int32_t* s2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" + "1: \n" +#else + "ldmia %[v1], {r0-r3} \n" + "ldmia %[f2]!, {r4-r7} \n" + "mul %[res], r4, r0 \n" + "mla %[res], r5, r1, %[res] \n" + "mla %[res], r6, r2, %[res] \n" + "mla %[res], r7, r3, %[res] \n" + "ldmia %[s2]!, {r4-r7} \n" + "add r0, r0, r4 \n" + "add r1, r1, r5 \n" + "add r2, r2, r6 \n" + "add r3, r3, r7 \n" + "stmia %[v1]!, {r0-r3} \n" +#endif + REPEAT_BLOCK( + "ldmia %[v1], {r0-r3} \n" + "ldmia %[f2]!, {r4-r7} \n" + "mla %[res], r4, r0, %[res] \n" + "mla %[res], r5, r1, %[res] \n" + "mla %[res], r6, r2, %[res] \n" + "mla %[res], r7, r3, %[res] \n" + "ldmia %[s2]!, {r4-r7} \n" + "add r0, r0, r4 \n" + "add r1, r1, r5 \n" + "add r2, r2, r6 \n" + "add r3, r3, r7 \n" + "stmia %[v1]!, {r0-r3} \n" + ) +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", + "r5", "r6", "r7", "cc", "memory" + ); + return res; +} + +/* Calculate scalarproduct, then subtract a 2nd vector (fused for performance) */ +static inline int32_t vector_sp_sub(int32_t* v1, int32_t* f2, int32_t* s2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" + "1: \n" +#else + "ldmia %[v1], {r0-r3} \n" + "ldmia %[f2]!, {r4-r7} \n" + "mul %[res], r4, r0 \n" + "mla %[res], r5, r1, %[res] \n" + "mla %[res], r6, r2, %[res] \n" + "mla %[res], r7, r3, %[res] \n" + "ldmia %[s2]!, {r4-r7} \n" + "sub r0, r0, r4 \n" + "sub r1, r1, r5 \n" + "sub r2, r2, r6 \n" + "sub r3, r3, r7 \n" + "stmia %[v1]!, {r0-r3} \n" +#endif + REPEAT_BLOCK( + "ldmia %[v1], {r0-r3} \n" + "ldmia %[f2]!, {r4-r7} \n" + "mla %[res], r4, r0, %[res] \n" + "mla %[res], r5, r1, %[res] \n" + "mla %[res], r6, r2, %[res] \n" + "mla %[res], r7, r3, %[res] \n" + "ldmia %[s2]!, {r4-r7} \n" + "sub r0, r0, r4 \n" + "sub r1, r1, r5 \n" + "sub r2, r2, r6 \n" + "sub r3, r3, r7 \n" + "stmia %[v1]!, {r0-r3} \n" + ) +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [f2] "+r"(f2), + [s2] "+r"(s2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", "r4", + "r5", "r6", "r7", "cc", "memory" + ); + return res; +} + +static inline int32_t scalarproduct(int32_t* v1, int32_t* v2) +{ + int res; +#if ORDER > 32 + int cnt = ORDER>>5; +#endif + + asm volatile ( +#if ORDER > 32 + "mov %[res], #0 \n" + "1: \n" +#else + "ldmia %[v1]!, {r0-r3} \n" + "ldmia %[v2]!, {r4-r7} \n" + "mul %[res], r4, r0 \n" + "mla %[res], r5, r1, %[res] \n" + "mla %[res], r6, r2, %[res] \n" + "mla %[res], r7, r3, %[res] \n" +#endif + REPEAT_BLOCK( + "ldmia %[v1]!, {r0-r3} \n" + "ldmia %[v2]!, {r4-r7} \n" + "mla %[res], r4, r0, %[res] \n" + "mla %[res], r5, r1, %[res] \n" + "mla %[res], r6, r2, %[res] \n" + "mla %[res], r7, r3, %[res] \n" + ) +#if ORDER > 32 + "subs %[cnt], %[cnt], #1 \n" + "bne 1b \n" +#endif + : /* outputs */ +#if ORDER > 32 + [cnt]"+r"(cnt), +#endif + [v1] "+r"(v1), + [v2] "+r"(v2), + [res]"=r"(res) + : /* inputs */ + : /* clobbers */ + "r0", "r1", "r2", "r3", + "r4", "r5", "r6", "r7", "cc", "memory" + ); + return res; +} diff --git a/lib/rbcodec/codecs/demac/libdemac/vector_math_generic.h b/lib/rbcodec/codecs/demac/libdemac/vector_math_generic.h new file mode 100644 index 0000000000..00bf07a007 --- /dev/null +++ b/lib/rbcodec/codecs/demac/libdemac/vector_math_generic.h @@ -0,0 +1,160 @@ +/* + +libdemac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include "demac_config.h" + +static inline void vector_add(filter_int* v1, filter_int* v2) +{ +#if ORDER > 32 + int order = (ORDER >> 5); + while (order--) +#endif + { + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; +#if ORDER > 16 + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; + *v1++ += *v2++; +#endif + } +} + +static inline void vector_sub(filter_int* v1, filter_int* v2) +{ +#if ORDER > 32 + int order = (ORDER >> 5); + while (order--) +#endif + { + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; +#if ORDER > 16 + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; + *v1++ -= *v2++; +#endif + } +} + +static inline int32_t scalarproduct(filter_int* v1, filter_int* v2) +{ + int res = 0; + +#if ORDER > 32 + int order = (ORDER >> 5); + while (order--) +#endif + { + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; +#if ORDER > 16 + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; + res += *v1++ * *v2++; +#endif + } + return res; +} diff --git a/lib/rbcodec/codecs/demac/wavwrite.c b/lib/rbcodec/codecs/demac/wavwrite.c new file mode 100644 index 0000000000..71d2b7bb97 --- /dev/null +++ b/lib/rbcodec/codecs/demac/wavwrite.c @@ -0,0 +1,110 @@ +/* + +demac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#include <stdio.h> +#include <inttypes.h> +#include <stdlib.h> +#include "inttypes.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#include "parser.h" + +#ifndef __WIN32__ +#define O_BINARY 0 +#endif + +static unsigned char wav_header[44]={ + 'R','I','F','F',// 0 - ChunkID + 0,0,0,0, // 4 - ChunkSize (filesize-8) + 'W','A','V','E',// 8 - Format + 'f','m','t',' ',// 12 - SubChunkID + 16,0,0,0, // 16 - SubChunk1ID // 16 for PCM + 1,0, // 20 - AudioFormat (1=Uncompressed) + 2,0, // 22 - NumChannels + 0,0,0,0, // 24 - SampleRate in Hz + 0,0,0,0, // 28 - Byte Rate (SampleRate*NumChannels*(BitsPerSample/8) + 4,0, // 32 - BlockAlign (== NumChannels * BitsPerSample/8) + 16,0, // 34 - BitsPerSample + 'd','a','t','a',// 36 - Subchunk2ID + 0,0,0,0 // 40 - Subchunk2Size +}; + +int open_wav(struct ape_ctx_t* ape_ctx, char* filename) +{ + int fd; + int x; + int filesize; + int bytespersample; + + fd=open(filename, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, 0644); + if (fd < 0) + return fd; + + bytespersample=ape_ctx->bps/8; + + filesize=ape_ctx->totalsamples*bytespersample*ape_ctx->channels+44; + + // ChunkSize + x=filesize-8; + wav_header[4]=(x&0xff); + wav_header[5]=(x&0xff00)>>8; + wav_header[6]=(x&0xff0000)>>16; + wav_header[7]=(x&0xff000000)>>24; + + // Number of channels + wav_header[22]=ape_ctx->channels; + + // Samplerate + wav_header[24]=ape_ctx->samplerate&0xff; + wav_header[25]=(ape_ctx->samplerate&0xff00)>>8; + wav_header[26]=(ape_ctx->samplerate&0xff0000)>>16; + wav_header[27]=(ape_ctx->samplerate&0xff000000)>>24; + + // ByteRate + x=ape_ctx->samplerate*(ape_ctx->bps/8)*ape_ctx->channels; + wav_header[28]=(x&0xff); + wav_header[29]=(x&0xff00)>>8; + wav_header[30]=(x&0xff0000)>>16; + wav_header[31]=(x&0xff000000)>>24; + + // BlockAlign + wav_header[32]=(ape_ctx->bps/8)*ape_ctx->channels; + + // Bits per sample + wav_header[34]=ape_ctx->bps; + + // Subchunk2Size + x=filesize-44; + wav_header[40]=(x&0xff); + wav_header[41]=(x&0xff00)>>8; + wav_header[42]=(x&0xff0000)>>16; + wav_header[43]=(x&0xff000000)>>24; + + write(fd,wav_header,sizeof(wav_header)); + + return fd; +} diff --git a/lib/rbcodec/codecs/demac/wavwrite.h b/lib/rbcodec/codecs/demac/wavwrite.h new file mode 100644 index 0000000000..a124353229 --- /dev/null +++ b/lib/rbcodec/codecs/demac/wavwrite.h @@ -0,0 +1,32 @@ +/* + +demac - A Monkey's Audio decoder + +$Id$ + +Copyright (C) Dave Chapman 2007 + +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 program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + +*/ + +#ifndef _APE_WAVWRITE_H +#define _APE_WAVWRITE_H + +#include "parser.h" + +int open_wav(struct ape_ctx_t* ape_ctx, char* filename); + +#endif diff --git a/lib/rbcodec/codecs/flac.c b/lib/rbcodec/codecs/flac.c new file mode 100644 index 0000000000..e10403819c --- /dev/null +++ b/lib/rbcodec/codecs/flac.c @@ -0,0 +1,536 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 Dave Chapman + * + * 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 <codecs/libffmpegFLAC/decoder.h> + +CODEC_HEADER + +static FLACContext fc IBSS_ATTR_FLAC; + +/* The output buffers containing the decoded samples (channels 0 and 1) */ +static int32_t decoded0[MAX_BLOCKSIZE] IBSS_ATTR_FLAC; +static int32_t decoded1[MAX_BLOCKSIZE] IBSS_ATTR_FLAC; +static int32_t decoded2[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_LARGE_IRAM; +static int32_t decoded3[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_LARGE_IRAM; +static int32_t decoded4[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_XLARGE_IRAM; +static int32_t decoded5[MAX_BLOCKSIZE] IBSS_ATTR_FLAC_XLARGE_IRAM; + +#define MAX_SUPPORTED_SEEKTABLE_SIZE 5000 + +/* Notes about seeking: + + The full seek table consists of: + uint64_t sample (only 36 bits are used) + uint64_t offset + uint32_t blocksize + + We also limit the sample and offset values to 32-bits - Rockbox doesn't + support files bigger than 2GB on FAT32 filesystems. + + The reference FLAC encoder produces a seek table with points every + 10 seconds, but this can be overridden by the user when encoding a file. + + With the default settings, a typical 4 minute track will contain + 24 seek points. + + Taking the extreme case of a Rockbox supported file to be a 2GB (compressed) + 16-bit/44.1KHz mono stream with a likely uncompressed size of 4GB: + Total duration is: 48694 seconds (about 810 minutes - 13.5 hours) + Total number of seek points: 4869 + + Therefore we limit the number of seek points to 5000. This is a + very extreme case, and requires 5000*8=40000 bytes of storage. + + If we come across a FLAC file with more than this number of seekpoints, we + just use the first 5000. + +*/ + +struct FLACseekpoints { + uint32_t sample; + uint32_t offset; + uint16_t blocksize; +}; + +static struct FLACseekpoints seekpoints[MAX_SUPPORTED_SEEKTABLE_SIZE]; +static int nseekpoints; + +static int8_t *bit_buffer; +static size_t buff_size; + +static bool flac_init(FLACContext* fc, int first_frame_offset) +{ + unsigned char buf[255]; + bool found_streaminfo=false; + uint32_t seekpoint_hi,seekpoint_lo; + uint32_t offset_hi,offset_lo; + uint16_t blocksize; + int endofmetadata=0; + uint32_t blocklength; + + ci->memset(fc,0,sizeof(FLACContext)); + nseekpoints=0; + + fc->sample_skip = 0; + + /* Reset sample buffers */ + memset(decoded0, 0, sizeof(decoded0)); + memset(decoded1, 0, sizeof(decoded1)); + memset(decoded2, 0, sizeof(decoded2)); + memset(decoded3, 0, sizeof(decoded3)); + memset(decoded4, 0, sizeof(decoded4)); + memset(decoded5, 0, sizeof(decoded5)); + + /* Set sample buffers in decoder structure */ + fc->decoded[0] = decoded0; + fc->decoded[1] = decoded1; + fc->decoded[2] = decoded2; + fc->decoded[3] = decoded3; + fc->decoded[4] = decoded4; + fc->decoded[5] = decoded5; + + + /* Skip any foreign tags at start of file */ + ci->seek_buffer(first_frame_offset); + + fc->metadatalength = first_frame_offset; + + if (ci->read_filebuf(buf, 4) < 4) + { + return false; + } + + if (ci->memcmp(buf,"fLaC",4) != 0) + { + return false; + } + fc->metadatalength += 4; + + while (!endofmetadata) { + if (ci->read_filebuf(buf, 4) < 4) + { + return false; + } + + endofmetadata=(buf[0]&0x80); + blocklength = (buf[1] << 16) | (buf[2] << 8) | buf[3]; + fc->metadatalength+=blocklength+4; + + if ((buf[0] & 0x7f) == 0) /* 0 is the STREAMINFO block */ + { + if (ci->read_filebuf(buf, blocklength) < blocklength) return false; + + fc->filesize = ci->filesize; + fc->min_blocksize = (buf[0] << 8) | buf[1]; + int max_blocksize = (buf[2] << 8) | buf[3]; + if (max_blocksize > MAX_BLOCKSIZE) + { + LOGF("FLAC: Maximum blocksize is too large (%d > %d)\n", + max_blocksize, MAX_BLOCKSIZE); + return false; + } + fc->max_blocksize = max_blocksize; + fc->min_framesize = (buf[4] << 16) | (buf[5] << 8) | buf[6]; + fc->max_framesize = (buf[7] << 16) | (buf[8] << 8) | buf[9]; + fc->samplerate = (buf[10] << 12) | (buf[11] << 4) + | ((buf[12] & 0xf0) >> 4); + fc->channels = ((buf[12]&0x0e)>>1) + 1; + fc->bps = (((buf[12]&0x01) << 4) | ((buf[13]&0xf0)>>4) ) + 1; + + /* totalsamples is a 36-bit field, but we assume <= 32 bits are + used */ + fc->totalsamples = (buf[14] << 24) | (buf[15] << 16) + | (buf[16] << 8) | buf[17]; + + /* Calculate track length (in ms) and estimate the bitrate + (in kbit/s) */ + fc->length = ((int64_t) fc->totalsamples * 1000) / fc->samplerate; + + found_streaminfo=true; + } else if ((buf[0] & 0x7f) == 3) { /* 3 is the SEEKTABLE block */ + while ((nseekpoints < MAX_SUPPORTED_SEEKTABLE_SIZE) && + (blocklength >= 18)) { + if (ci->read_filebuf(buf,18) < 18) return false; + blocklength-=18; + + seekpoint_hi=(buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]; + seekpoint_lo=(buf[4] << 24) | (buf[5] << 16) | + (buf[6] << 8) | buf[7]; + offset_hi=(buf[8] << 24) | (buf[9] << 16) | + (buf[10] << 8) | buf[11]; + offset_lo=(buf[12] << 24) | (buf[13] << 16) | + (buf[14] << 8) | buf[15]; + + blocksize=(buf[16] << 8) | buf[17]; + + /* Only store seekpoints where the high 32 bits are zero */ + if ((seekpoint_hi == 0) && (seekpoint_lo != 0xffffffff) && + (offset_hi == 0)) { + seekpoints[nseekpoints].sample=seekpoint_lo; + seekpoints[nseekpoints].offset=offset_lo; + seekpoints[nseekpoints].blocksize=blocksize; + nseekpoints++; + } + } + /* Skip any unread seekpoints */ + if (blocklength > 0) + ci->advance_buffer(blocklength); + } else { + /* Skip to next metadata block */ + ci->advance_buffer(blocklength); + } + } + + if (found_streaminfo) { + fc->bitrate = ((int64_t) (fc->filesize-fc->metadatalength) * 8) + / fc->length; + return true; + } else { + return false; + } +} + +/* Synchronize to next frame in stream - adapted from libFLAC 1.1.3b2 */ +static bool frame_sync(FLACContext* fc) { + unsigned int x = 0; + bool cached = false; + + /* Make sure we're byte aligned. */ + align_get_bits(&fc->gb); + + while(1) { + if(fc->gb.size_in_bits - get_bits_count(&fc->gb) < 8) { + /* Error, end of bitstream, a valid stream should never reach here + * since the buffer should contain at least one frame header. + */ + return false; + } + + if(cached) + cached = false; + else + x = get_bits(&fc->gb, 8); + + if(x == 0xff) { /* MAGIC NUMBER for first 8 frame sync bits. */ + x = get_bits(&fc->gb, 8); + /* We have to check if we just read two 0xff's in a row; the second + * may actually be the beginning of the sync code. + */ + if(x == 0xff) { /* MAGIC NUMBER for first 8 frame sync bits. */ + cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for last 6 sync bits. */ + /* Succesfully synced. */ + break; + } + } + } + + /* Advance and init bit buffer to the new frame. */ + ci->advance_buffer((get_bits_count(&fc->gb)-16)>>3); /* consumed bytes */ + bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE+16); + init_get_bits(&fc->gb, bit_buffer, buff_size*8); + + /* Decode the frame to verify the frame crc and + * fill fc with its metadata. + */ + if(flac_decode_frame(fc, + bit_buffer, buff_size, ci->yield) < 0) { + return false; + } + + return true; +} + +/* Seek to sample - adapted from libFLAC 1.1.3b2+ */ +static bool flac_seek(FLACContext* fc, uint32_t target_sample) { + off_t orig_pos = ci->curpos; + off_t pos = -1; + unsigned long lower_bound, upper_bound; + unsigned long lower_bound_sample, upper_bound_sample; + int i; + unsigned approx_bytes_per_frame; + uint32_t this_frame_sample = fc->samplenumber; + unsigned this_block_size = fc->blocksize; + bool needs_seek = true, first_seek = true; + + /* We are just guessing here. */ + if(fc->max_framesize > 0) + approx_bytes_per_frame = (fc->max_framesize + fc->min_framesize)/2 + 1; + /* Check if it's a known fixed-blocksize stream. */ + else if(fc->min_blocksize == fc->max_blocksize && fc->min_blocksize > 0) + approx_bytes_per_frame = fc->min_blocksize*fc->channels*fc->bps/8 + 64; + else + approx_bytes_per_frame = 4608 * fc->channels * fc->bps/8 + 64; + + /* Set an upper and lower bound on where in the stream we will search. */ + lower_bound = fc->metadatalength; + lower_bound_sample = 0; + upper_bound = fc->filesize; + upper_bound_sample = fc->totalsamples>0 ? fc->totalsamples : target_sample; + + /* Refine the bounds if we have a seektable with suitable points. */ + if(nseekpoints > 0) { + /* Find the closest seek point <= target_sample, if it exists. */ + for(i = nseekpoints-1; i >= 0; i--) { + if(seekpoints[i].sample <= target_sample) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + lower_bound = fc->metadatalength + seekpoints[i].offset; + lower_bound_sample = seekpoints[i].sample; + } + + /* Find the closest seek point > target_sample, if it exists. */ + for(i = 0; i < nseekpoints; i++) { + if(seekpoints[i].sample > target_sample) + break; + } + if(i < nseekpoints) { /* i.e. we found a suitable seek point... */ + upper_bound = fc->metadatalength + seekpoints[i].offset; + upper_bound_sample = seekpoints[i].sample; + } + } + + while(1) { + /* Check if bounds are still ok. */ + if(lower_bound_sample >= upper_bound_sample || + lower_bound > upper_bound) { + return false; + } + + /* Calculate new seek position */ + if(needs_seek) { + pos = (off_t)(lower_bound + + (((target_sample - lower_bound_sample) * + (int64_t)(upper_bound - lower_bound)) / + (upper_bound_sample - lower_bound_sample)) - + approx_bytes_per_frame); + + if(pos >= (off_t)upper_bound) + pos = (off_t)upper_bound-1; + if(pos < (off_t)lower_bound) + pos = (off_t)lower_bound; + } + + if(!ci->seek_buffer(pos)) + return false; + + bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE+16); + init_get_bits(&fc->gb, bit_buffer, buff_size*8); + + /* Now we need to get a frame. It is possible for our seek + * to land in the middle of audio data that looks exactly like + * a frame header from a future version of an encoder. When + * that happens, frame_sync() will return false. + * But there is a remote possibility that it is properly + * synced at such a "future-codec frame", so to make sure, + * we wait to see several "unparseable" errors in a row before + * bailing out. + */ + { + unsigned unparseable_count; + bool got_a_frame = false; + for(unparseable_count = 0; !got_a_frame + && unparseable_count < 10; unparseable_count++) { + if(frame_sync(fc)) + got_a_frame = true; + } + if(!got_a_frame) { + ci->seek_buffer(orig_pos); + return false; + } + } + + this_frame_sample = fc->samplenumber; + this_block_size = fc->blocksize; + + if(target_sample >= this_frame_sample + && target_sample < this_frame_sample+this_block_size) { + /* Found the frame containing the target sample. */ + fc->sample_skip = target_sample - this_frame_sample; + break; + } + + if(this_frame_sample + this_block_size >= upper_bound_sample && + !first_seek) { + if(pos == (off_t)lower_bound || !needs_seek) { + ci->seek_buffer(orig_pos); + return false; + } + /* Our last move backwards wasn't big enough, try again. */ + approx_bytes_per_frame *= 2; + continue; + } + /* Allow one seek over upper bound, + * required for streams with unknown total samples. + */ + first_seek = false; + + /* Make sure we are not seeking in a corrupted stream */ + if(this_frame_sample < lower_bound_sample) { + ci->seek_buffer(orig_pos); + return false; + } + + approx_bytes_per_frame = this_block_size*fc->channels*fc->bps/8 + 64; + + /* We need to narrow the search. */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample; + upper_bound = ci->curpos; + } + else { /* Target is beyond this frame. */ + /* We are close, continue in decoding next frames. */ + if(target_sample < this_frame_sample + 4*this_block_size) { + pos = ci->curpos + fc->framesize; + needs_seek = false; + } + + lower_bound_sample = this_frame_sample + this_block_size; + lower_bound = ci->curpos + fc->framesize; + } + } + + return true; +} + +/* Seek to file offset */ +static bool flac_seek_offset(FLACContext* fc, uint32_t offset) { + unsigned unparseable_count; + bool got_a_frame = false; + + if(!ci->seek_buffer(offset)) + return false; + + bit_buffer = ci->request_buffer(&buff_size, MAX_FRAMESIZE); + init_get_bits(&fc->gb, bit_buffer, buff_size*8); + + for(unparseable_count = 0; !got_a_frame + && unparseable_count < 10; unparseable_count++) { + if(frame_sync(fc)) + got_a_frame = true; + } + + if(!got_a_frame) { + ci->seek_buffer(fc->metadatalength); + return false; + } + + return true; +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* Generic codec initialisation */ + ci->configure(DSP_SET_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + int8_t *buf; + uint32_t samplesdone; + uint32_t elapsedtime; + size_t bytesleft; + int consumed; + int res; + int frame; + intptr_t param; + + if (codec_init()) { + LOGF("FLAC: Error initialising codec\n"); + return CODEC_ERROR; + } + + /* Need to save offset for later use (cleared indirectly by flac_init) */ + samplesdone = ci->id3->offset; + + if (!flac_init(&fc,ci->id3->first_frame_offset)) { + LOGF("FLAC: Error initialising codec\n"); + return CODEC_ERROR; + } + + ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); + ci->configure(DSP_SET_STEREO_MODE, fc.channels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + codec_set_replaygain(ci->id3); + + flac_seek_offset(&fc, samplesdone); + samplesdone=fc.samplenumber+fc.blocksize; + elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); + ci->set_elapsed(elapsedtime); + + /* The main decoding loop */ + frame=0; + buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); + while (bytesleft) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + /* Deal with any pending seek requests */ + if (action == CODEC_ACTION_SEEK_TIME) { + if (flac_seek(&fc,(uint32_t)(((uint64_t)param + *ci->id3->frequency)/1000))) { + /* Refill the input buffer */ + buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); + } + + ci->set_elapsed(param); + ci->seek_complete(); + } + + if((res=flac_decode_frame(&fc,buf, + bytesleft,ci->yield)) < 0) { + LOGF("FLAC: Frame %d, error %d\n",frame,res); + return CODEC_ERROR; + } + consumed=fc.gb.index/8; + frame++; + + ci->yield(); + ci->pcmbuf_insert(&fc.decoded[0][fc.sample_skip], &fc.decoded[1][fc.sample_skip], + fc.blocksize - fc.sample_skip); + + fc.sample_skip = 0; + + /* Update the elapsed-time indicator */ + samplesdone=fc.samplenumber+fc.blocksize; + elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); + ci->set_elapsed(elapsedtime); + + ci->advance_buffer(consumed); + + buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); + } + + LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone); + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/gbs.c b/lib/rbcodec/codecs/gbs.c new file mode 100644 index 0000000000..def05ed351 --- /dev/null +++ b/lib/rbcodec/codecs/gbs.c @@ -0,0 +1,108 @@ + +/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ + +#include <codecs/lib/codeclib.h> +#include "libgme/gbs_emu.h" + +CODEC_HEADER + +/* Maximum number of bytes to process in one iteration */ +#define CHUNK_SIZE (1024*2) + +static int16_t samples[CHUNK_SIZE] IBSS_ATTR; +static struct Gbs_Emu gbs_emu; + +/****************** rockbox interface ******************/ + +static void set_codec_track(int t) { + Gbs_start_track(&gbs_emu, t); + + /* for loop mode we disable track limits */ + if (!ci->loop_track()) { + Track_set_fade(&gbs_emu, Track_get_length( &gbs_emu, t ), 4000); + } + ci->set_elapsed(t*1000); /* t is track no to display */ +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* we only render 16 bits */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + + /* 44 Khz, Interleaved stereo */ + ci->configure(DSP_SET_FREQUENCY, 44100); + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + + Gbs_init(&gbs_emu); + Gbs_set_sample_rate(&gbs_emu, 44100); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + blargg_err_t err; + uint8_t *buf; + size_t n; + intptr_t param; + int track = 0; + + DEBUGF("GBS: next_track\n"); + if (codec_init()) { + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Read the entire file */ + DEBUGF("GBS: request file\n"); + ci->seek_buffer(0); + buf = ci->request_buffer(&n, ci->filesize); + if (!buf || n < (size_t)ci->filesize) { + DEBUGF("GBS: file load failed\n"); + return CODEC_ERROR; + } + + if ((err = Gbs_load_mem(&gbs_emu, buf, ci->filesize))) { + DEBUGF("GBS: Gbs_load_mem failed (%s)\n", err); + return CODEC_ERROR; + } + + /* Update internal track count */ + if (gbs_emu.m3u.size > 0) + gbs_emu.track_count = gbs_emu.m3u.size; + +next_track: + set_codec_track(track); + + /* The main decoder loop */ + while (1) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + if (action == CODEC_ACTION_SEEK_TIME) { + track = param/1000; + ci->seek_complete(); + if (track >= gbs_emu.track_count) break; + goto next_track; + } + + /* Generate audio buffer */ + err = Gbs_play(&gbs_emu, CHUNK_SIZE, samples); + if (err || Track_ended(&gbs_emu)) { + track++; + if (track >= gbs_emu.track_count) break; + goto next_track; + } + + ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1); + } + + return CODEC_OK; +} diff --git a/lib/rbcodec/codecs/hes.c b/lib/rbcodec/codecs/hes.c new file mode 100644 index 0000000000..849fd88f12 --- /dev/null +++ b/lib/rbcodec/codecs/hes.c @@ -0,0 +1,108 @@ +/* Ripped off from Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ + +#include <string.h> +#include "codeclib.h" +#include "libgme/hes_emu.h" + +CODEC_HEADER + +/* Maximum number of bytes to process in one iteration */ +#define CHUNK_SIZE (1024*2) + +static int16_t samples[CHUNK_SIZE] IBSS_ATTR; +static struct Hes_Emu hes_emu; + +/****************** rockbox interface ******************/ + +static void set_codec_track(int t) { + Hes_start_track(&hes_emu, t); + + /* for loop mode we disable track limits */ + if (!ci->loop_track()) { + Track_set_fade(&hes_emu, Track_get_length( &hes_emu, t ), 4000); + } + ci->set_elapsed(t*1000); /* t is track no to display */ +} + +/* this is the codec entry point */ +enum codec_status codec_main(enum codec_entry_call_reason reason) +{ + if (reason == CODEC_LOAD) { + /* we only render 16 bits */ + ci->configure(DSP_SET_SAMPLE_DEPTH, 16); + + /* 44 Khz, Interleaved stereo */ + ci->configure(DSP_SET_FREQUENCY, 44100); + ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); + + Hes_init(&hes_emu); + Hes_set_sample_rate(&hes_emu, 44100); + } + + return CODEC_OK; +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + blargg_err_t err; + uint8_t *buf; + size_t n; + intptr_t param; + int track = 0; + + DEBUGF("HES: next_track\n"); + if (codec_init()) { + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Read the entire file */ + DEBUGF("HES: request file\n"); + ci->seek_buffer(0); + buf = ci->request_buffer(&n, ci->filesize); + if (!buf || n < (size_t)ci->filesize) { + DEBUGF("HES: file load failed\n"); + return CODEC_ERROR; + } + + if ((err = Hes_load_mem(&hes_emu, buf, ci->filesize))) { + DEBUGF("HES: Hes_load_mem failed (%s)\n", err); + return CODEC_ERROR; + } + + /* Update internal track count */ + if (hes_emu.m3u.size > 0) + hes_emu.track_count = hes_emu.m3u.size; + +next_track: + set_codec_track(track); + + /* The main decoder loop */ + while ( 1 ) { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) |