From f40bfc9267b13b54e6379dfe7539447662879d24 Mon Sep 17 00:00:00 2001 From: Sean Bartell Date: Sat, 25 Jun 2011 21:32:25 -0400 Subject: Add codecs to librbcodec. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97 Reviewed-on: http://gerrit.rockbox.org/137 Reviewed-by: Nils Wallménius Tested-by: Nils Wallménius --- lib/rbcodec/codecs/libgme/hes_apu_adpcm.c | 297 ++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 lib/rbcodec/codecs/libgme/hes_apu_adpcm.c (limited to 'lib/rbcodec/codecs/libgme/hes_apu_adpcm.c') diff --git a/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c b/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c new file mode 100644 index 0000000000..de9b894f5d --- /dev/null +++ b/lib/rbcodec/codecs/libgme/hes_apu_adpcm.c @@ -0,0 +1,297 @@ +// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/ + +#include "hes_apu_adpcm.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module 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 Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + + +void Adpcm_init( struct Hes_Apu_Adpcm* this ) +{ + this->output = NULL; + memset( &this->state, 0, sizeof( this->state ) ); + Adpcm_reset( this ); +} + +void Adpcm_reset( struct Hes_Apu_Adpcm* this ) +{ + this->last_time = 0; + this->next_timer = 0; + this->last_amp = 0; + + memset( &this->state.pcmbuf, 0, sizeof(this->state.pcmbuf) ); + memset( &this->state.port, 0, sizeof(this->state.port) ); + + this->state.ad_sample = 0; + this->state.ad_ref_index = 0; + + this->state.addr = 0; + this->state.freq = 0; + this->state.writeptr = 0; + this->state.readptr = 0; + this->state.playflag = 0; + this->state.repeatflag = 0; + this->state.length = 0; + this->state.volume = 0xFF; + this->state.fadetimer = 0; + this->state.fadecount = 0; +} + +static short stepsize[49] = { + 16, 17, 19, 21, 23, 25, 28, + 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, + 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, + 876, 963,1060,1166,1282,1411,1552 +}; + +static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code ); +static int Adpcm_decode( struct Hes_Apu_Adpcm* this,int code ) +{ + struct State* state = &this->state; + int step = stepsize[state->ad_ref_index]; + int delta; + int c = code & 7; +#if 1 + delta = 0; + if ( c & 4 ) delta += step; + step >>= 1; + if ( c & 2 ) delta += step; + step >>= 1; + if ( c & 1 ) delta += step; + step >>= 1; + delta += step; +#else + delta = ( ( c + c + 1 ) * step ) / 8; // maybe faster, but introduces rounding +#endif + if ( c != code ) + { + state->ad_sample -= delta; + if ( state->ad_sample < -2048 ) + state->ad_sample = -2048; + } + else + { + state->ad_sample += delta; + if ( state->ad_sample > 2047 ) + state->ad_sample = 2047; + } + + static int const steps [8] = { + -1, -1, -1, -1, 2, 4, 6, 8 + }; + state->ad_ref_index += steps [c]; + if ( state->ad_ref_index < 0 ) + state->ad_ref_index = 0; + else if ( state->ad_ref_index > 48 ) + state->ad_ref_index = 48; + + return state->ad_sample; +} + +static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time ); +static void Adpcm_run_until( struct Hes_Apu_Adpcm* this, blip_time_t end_time ) +{ + struct State* state = &this->state; + int volume = state->volume; + int fadetimer = state->fadetimer; + int fadecount = state->fadecount; + int last_time = this->last_time; + int next_timer = this->next_timer; + int last_amp = this->last_amp; + + struct Blip_Buffer* output = this->output; // cache often-used values + + while ( state->playflag && last_time < end_time ) + { + while ( last_time >= next_timer ) + { + if ( fadetimer ) + { + if ( fadecount > 0 ) + { + fadecount--; + volume = 0xFF * fadecount / fadetimer; + } + else if ( fadecount < 0 ) + { + fadecount++; + volume = 0xFF - ( 0xFF * fadecount / fadetimer ); + } + } + next_timer += 7159; // 7159091/1000; + } + int amp; + if ( state->ad_low_nibble ) + { + amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] & 0x0F ); + state->ad_low_nibble = false; + state->playptr++; + state->playedsamplecount++; + if ( state->playedsamplecount == state->playlength ) + { + state->playflag = 0; + } + } + else + { + amp = Adpcm_decode( this, state->pcmbuf[ state->playptr ] >> 4 ); + state->ad_low_nibble = true; + } + amp = amp * volume / 0xFF; + int delta = amp - last_amp; + if ( output && delta ) + { + last_amp = amp; + Synth_offset_inline( &this->synth, last_time, delta, output ); + } + last_time += state->freq; + } + + if ( !state->playflag ) + { + while ( next_timer <= end_time ) next_timer += 7159; // 7159091/1000 + last_time = end_time; + } + + this->last_time = last_time; + this->next_timer = next_timer; + this->last_amp = last_amp; + state->volume = volume; + state->fadetimer = fadetimer; + state->fadecount = fadecount; +} + +void Adpcm_write_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr, int data ) +{ + if ( time > this->last_time ) Adpcm_run_until( this, time ); + struct State* state = &this->state; + + data &= 0xFF; + state->port[ addr & 15 ] = data; + switch ( addr & 15 ) + { + case 8: + state->addr &= 0xFF00; + state->addr |= data; + break; + case 9: + state->addr &= 0xFF; + state->addr |= data << 8; + break; + case 10: + state->pcmbuf[ state->writeptr++ ] = data; + state->playlength ++; + break; + case 11: + dprintf("ADPCM DMA 0x%02X", data); + break; + case 13: + if ( data & 0x80 ) + { + state->addr = 0; + state->freq = 0; + state->writeptr = 0; + state->readptr = 0; + state->playflag = 0; + state->repeatflag = 0; + state->length = 0; + state->volume = 0xFF; + } + if ( ( data & 3 ) == 3 ) + { + state->writeptr = state->addr; + } + if ( data & 8 ) + { + state->readptr = state->addr ? state->addr - 1 : state->addr; + } + if ( data & 0x10 ) + { + state->length = state->addr; + } + state->repeatflag = data & 0x20; + state->playflag = data & 0x40; + if ( state->playflag ) + { + state->playptr = state->readptr; + state->playlength = state->length + 1; + state->playedsamplecount = 0; + state->ad_sample = 0; + state->ad_low_nibble = false; + } + break; + case 14: + state->freq = 7159091 / ( 32000 / ( 16 - ( data & 15 ) ) ); + break; + case 15: + switch ( data & 15 ) + { + case 0: + case 8: + case 12: + state->fadetimer = -100; + state->fadecount = state->fadetimer; + break; + case 10: + state->fadetimer = 5000; + state->fadecount = state->fadetimer; + break; + case 14: + state->fadetimer = 1500; + state->fadecount = state->fadetimer; + break; + } + break; + } +} + +int Adpcm_read_data( struct Hes_Apu_Adpcm* this, blip_time_t time, int addr ) +{ + if ( time > this->last_time ) Adpcm_run_until( this, time ); + + struct State* state = &this->state; + switch ( addr & 15 ) + { + case 10: + return state->pcmbuf [state->readptr++]; + case 11: + return state->port [11] & ~1; + case 12: + if (!state->playflag) + { + state->port [12] |= 1; + state->port [12] &= ~8; + } + else + { + state->port [12] &= ~1; + state->port [12] |= 8; + } + return state->port [12]; + case 13: + return state->port [13]; + } + + return 0xFF; +} + +void Adpcm_end_frame( struct Hes_Apu_Adpcm* this, blip_time_t end_time ) +{ + Adpcm_run_until( this, end_time ); + this->last_time -= end_time; + this->next_timer -= end_time; + check( last_time >= 0 ); + if ( this->output ) + Blip_set_modified( this->output ); +} -- cgit v1.2.3