summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp')
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp309
1 files changed, 309 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp
new file mode 100644
index 00000000..14eb6910
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp
@@ -0,0 +1,309 @@
+// 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 */
+
+#include "blargg_source.h"
+
+Hes_Apu_Adpcm::Hes_Apu_Adpcm()
+{
+ output = NULL;
+
+ memset( &state, 0, sizeof( state ) );
+
+ reset();
+}
+
+void Hes_Apu_Adpcm::reset()
+{
+ last_time = 0;
+ next_timer = 0;
+ last_amp = 0;
+
+ memset( &state.pcmbuf, 0, sizeof(state.pcmbuf) );
+ memset( &state.port, 0, sizeof(state.port) );
+
+ state.ad_sample = 0;
+ state.ad_ref_index = 0;
+
+ state.addr = 0;
+ state.freq = 0;
+ state.writeptr = 0;
+ state.readptr = 0;
+ state.playflag = 0;
+ state.repeatflag = 0;
+ state.length = 0;
+ state.volume = 0xFF;
+ state.fadetimer = 0;
+ state.fadecount = 0;
+}
+
+void Hes_Apu_Adpcm::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ output = center;
+}
+
+void Hes_Apu_Adpcm::run_until( blip_time_t end_time )
+{
+ int volume = state.volume;
+ int fadetimer = state.fadetimer;
+ int fadecount = state.fadecount;
+ int last_time = this->last_time;
+ double next_timer = this->next_timer;
+ int last_amp = this->last_amp;
+
+ 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.091;
+ }
+ int amp;
+ if ( state.ad_low_nibble )
+ {
+ amp = adpcm_decode( 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( 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( last_time, delta, output );
+ }
+ last_time += state.freq;
+ }
+
+ if ( !state.playflag )
+ {
+ while ( next_timer <= end_time ) next_timer += 7159.091;
+ 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 Hes_Apu_Adpcm::write_data( blip_time_t time, int addr, int data )
+{
+ if ( time > last_time ) run_until( time );
+
+ 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 Hes_Apu_Adpcm::read_data( blip_time_t time, int addr )
+{
+ if ( time > last_time ) run_until( time );
+
+ 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 Hes_Apu_Adpcm::end_frame( blip_time_t end_time )
+{
+ run_until( end_time );
+ last_time -= end_time;
+ next_timer -= (double)end_time;
+ check( last_time >= 0 );
+ if ( output )
+ output->set_modified();
+}
+
+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
+};
+
+int Hes_Apu_Adpcm::adpcm_decode( int code )
+{
+ 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;
+} \ No newline at end of file