/* ** ** File: ymdeltat.c ** ** YAMAHA DELTA-T adpcm sound emulation subroutine ** used by fmopl.c(v0.36e-) and fm.c(v0.36c-) ** ** Base program is YM2610 emulator by Hiromitsu Shioya. ** Written by Tatsuyuki Satoh ** ** Version 0.36b ** ** sound chips who has this unit ** ** YM2608 OPNA ** YM2610/B OPNB ** Y8950 MSX AUDIO ** ** */ #ifndef YM_INLINE_BLOCK // #include "neo_snd.h" #include "fm.h" #include "ymdeltat.h" #include "../state.h" Uint8 *ym_deltat_memory; /* memory pointer */ /* Forecast to next Forecast (rate = *8) */ /* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */ const Sint32 ym_deltat_decode_tableB1[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, }; /* delta to next delta (rate= *64) */ /* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */ const Sint32 ym_deltat_decode_tableB2[16] = { 57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153 }; /* DELTA-T-ADPCM write register */ void YM_DELTAT_ADPCM_Write(YM_DELTAT * DELTAT, int r, int v) { if (r >= 0x10) return; DELTAT->reg[r] = v; /* stock data */ switch (r) { case 0x00: /* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */ #if 0 case 0x60: /* write buffer MEMORY from PCM data port */ case 0x20: /* read buffer MEMORY to PCM data port */ #endif if (v & 0x80) { DELTAT->portstate = v & 0x90; /* start req,memory mode,repeat flag copy */ /**** start ADPCM ****/ DELTAT->volume_w_step = (double) DELTAT->volume * DELTAT->step / (1 << YM_DELTAT_SHIFT); DELTAT->now_addr = (DELTAT->start) << 1; DELTAT->now_step = (1 << YM_DELTAT_SHIFT) - DELTAT->step; /*adpcm->adpcmm = 0; */ DELTAT->adpcmx = 0; DELTAT->adpcml = 0; DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; DELTAT->next_leveling = 0; DELTAT->flag = 1; /* start ADPCM */ if (!DELTAT->step) { DELTAT->flag = 0; DELTAT->portstate = 0x00; } /**** PCM memory check & limit check ****/ if (DELTAT->memory == 0) { /* Check memory Mapped */ /*Log(LOG_ERR,"YM Delta-T ADPCM rom not mapped\n"); */ DELTAT->flag = 0; DELTAT->portstate = 0x00; /*logerror("DELTAT memory 0\n"); */ } else { if (DELTAT->end >= DELTAT->memory_size) { /* Check End in Range */ /*Log(LOG_ERR,"YM Delta-T ADPCM end out of range: $%08x\n",DELTAT->end); */ DELTAT->end = DELTAT->memory_size - 1; /*logerror("DELTAT end over\n"); */ } if (DELTAT->start >= DELTAT->memory_size) { /* Check Start in Range */ /*Log(LOG_ERR,"YM Delta-T ADPCM start out of range: $%08x\n",DELTAT->start); */ DELTAT->flag = 0; DELTAT->portstate = 0x00; /*logerror("DELTAT start under\n"); */ } } } else if (v & 0x01) { DELTAT->flag = 0; DELTAT->portstate = 0x00; } break; case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */ DELTAT->portcontrol = v & 0xff; DELTAT->pan = &DELTAT->output_pointer[(v >> 6) & 0x03]; break; case 0x02: /* Start Address L */ case 0x03: /* Start Address H */ DELTAT->start = (DELTAT->reg[0x3] * 0x0100 | DELTAT->reg[0x2]) << DELTAT->portshift; break; case 0x04: /* Stop Address L */ case 0x05: /* Stop Address H */ DELTAT->end = (DELTAT->reg[0x5] * 0x0100 | DELTAT->reg[0x4]) << DELTAT->portshift; DELTAT->end += (1 << DELTAT->portshift) - 1; break; case 0x06: /* Prescale L (PCM and Recoard frq) */ case 0x07: /* Proscale H */ case 0x08: /* ADPCM data */ break; case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */ case 0x0a: /* DELTA-N H */ DELTAT->delta = (DELTAT->reg[0xa] * 0x0100 | DELTAT->reg[0x9]); DELTAT->step = (Uint32) ((double) (DELTAT->delta * (1 << (YM_DELTAT_SHIFT - 16))) * (DELTAT->freqbase)); DELTAT->volume_w_step = (double) DELTAT->volume * DELTAT->step / (1 << YM_DELTAT_SHIFT); break; case 0x0b: /* Level control (volume , voltage flat) */ { Sint32 oldvol = DELTAT->volume; DELTAT->volume = (v & 0xff) * (DELTAT->output_range / 256) / YM_DELTAT_DECODE_RANGE; if (oldvol != 0) { DELTAT->adpcml = (int) ((double) DELTAT->adpcml / (double) oldvol * (double) DELTAT->volume); DELTAT->sample_step = (int) ((double) DELTAT->sample_step / (double) oldvol * (double) DELTAT->volume); } DELTAT->volume_w_step = (int) ((double) DELTAT->volume * (double) DELTAT->step / (double) (1 << YM_DELTAT_SHIFT)); } break; } } void YM_DELTAT_ADPCM_Reset(YM_DELTAT * DELTAT, int pan) { DELTAT->now_addr = 0; DELTAT->now_step = 0; DELTAT->step = 0; DELTAT->start = 0; DELTAT->end = 0; /* F2610->adpcm[i].delta = 21866; */ DELTAT->volume = 0; DELTAT->pan = &DELTAT->output_pointer[pan]; /* DELTAT->flagMask = 0; */ DELTAT->arrivedFlag = 0; DELTAT->flag = 0; DELTAT->adpcmx = 0; DELTAT->adpcmd = 127; DELTAT->adpcml = 0; /*DELTAT->adpcmm = 0; */ DELTAT->volume_w_step = 0; DELTAT->next_leveling = 0; DELTAT->portstate = 0; /* DELTAT->portshift = 8; */ } void YM_DELTAT_postload(YM_DELTAT *DELTAT,Uint8 *regs) { int r; /* to keep adpcml and resample_step */ DELTAT->volume = 0; /* update */ for(r=1;r<16;r++) YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]); DELTAT->reg[0] = regs[0]; /* current rom data */ DELTAT->now_data = *(ym_deltat_memory+(DELTAT->now_addr>>1)); } void adpcmb_post_load(void) { YM2610 *F2610 = &(FM2610); YM_DELTAT_postload(&F2610->deltaT , &F2610->REGS[0x100] ); } void YM_DELTAT_savestate(YM_DELTAT *DELTAT) { create_state_register(ST_YM2610_ADPCMB, "DT.portstate" ,1, &DELTAT->portstate , 1*sizeof(Uint8),REG_UINT8); create_state_register(ST_YM2610_ADPCMB, "DT.address" ,1, &DELTAT->now_addr , 1*sizeof(Uint32),REG_UINT32); create_state_register(ST_YM2610_ADPCMB, "DT.step" ,1, &DELTAT->now_step , 1*sizeof(Uint32),REG_UINT32); create_state_register(ST_YM2610_ADPCMB, "DT.adpcmx" ,1, &DELTAT->adpcmx , 1*sizeof(Sint32),REG_INT32); create_state_register(ST_YM2610_ADPCMB, "DT.adpcmd" ,1, &DELTAT->adpcmd , 1*sizeof(Sint32),REG_INT32); create_state_register(ST_YM2610_ADPCMB, "DT.adpcml" ,1, &DELTAT->adpcml , 1*sizeof(Sint32),REG_INT32); create_state_register(ST_YM2610_ADPCMB, "DT.next_leveling",1, &DELTAT->next_leveling , 1*sizeof(Sint32),REG_INT32); create_state_register(ST_YM2610_ADPCMB, "DT.sample_step",1, &DELTAT->sample_step , 1*sizeof(Sint32),REG_INT32); set_post_load_function(ST_YM2610_ADPCMB,adpcmb_post_load); } //#else /* YM_INLINE_BLOCK */ /* ---------- inline block ---------- */ /* DELTA-T particle adjuster */ /* #define YM_DELTAT_DELTA_MAX (24576) #define YM_DELTAT_DELTA_MIN (127) #define YM_DELTAT_DELTA_DEF (127) #define YM_DELTAT_DECODE_RANGE 32768 #define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE)) #define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1) extern const Sint32 ym_deltat_decode_tableB1[]; extern const Sint32 ym_deltat_decode_tableB2[]; #define YM_DELTAT_Limit(val,max,min) \ { \ if ( val > max ) val = max; \ else if ( val < min ) val = min;\ } */ /**** ADPCM B (Delta-T control type) ****/ inline void YM_DELTAT_ADPCM_CALC(YM_DELTAT * DELTAT) { Uint32 step; int data; Sint32 old_m; Sint32 now_leveling; Sint32 delta_next; DELTAT->now_step += DELTAT->step; if (DELTAT->now_step >= (1 << YM_DELTAT_SHIFT)) { step = DELTAT->now_step >> YM_DELTAT_SHIFT; DELTAT->now_step &= (1 << YM_DELTAT_SHIFT) - 1; do { if (DELTAT->now_addr > (DELTAT->end << 1)) { if (DELTAT->portstate & 0x10) { /**** repeat start ****/ DELTAT->now_addr = DELTAT->start << 1; DELTAT->adpcmx = 0; DELTAT->adpcmd = YM_DELTAT_DELTA_DEF; DELTAT->next_leveling = 0; DELTAT->flag = 1; } else { DELTAT->arrivedFlag |= DELTAT->flagMask; DELTAT->flag = 0; DELTAT->adpcml = 0; now_leveling = 0; return; } } if (DELTAT->now_addr & 1) data = DELTAT->now_data & 0x0f; else { DELTAT->now_data = *(ym_deltat_memory + (DELTAT->now_addr >> 1)); data = DELTAT->now_data >> 4; } DELTAT->now_addr++; /* shift Measurement value */ old_m = DELTAT->adpcmx /*adpcmm */ ; /* ch->adpcmm = YM_DELTAT_Limit( ch->adpcmx + (decode_tableB3[data] * ch->adpcmd / 8) ,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN ); */ /* Forecast to next Forecast */ DELTAT->adpcmx += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8); YM_DELTAT_Limit(DELTAT->adpcmx, YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN); /* delta to next delta */ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data]) / 64; YM_DELTAT_Limit(DELTAT->adpcmd, YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN); /* shift leveling value */ delta_next = DELTAT->adpcmx /*adpcmm */ - old_m; now_leveling = DELTAT->next_leveling; DELTAT->next_leveling = old_m + (delta_next / 2); } while (--step); //#define YM_DELTAT_CUT_RE_SAMPLING #ifdef YM_DELTAT_CUT_RE_SAMPLING DELTAT->adpcml = DELTAT->next_leveling * DELTAT->volume; DELTAT->adpcml = DELTAT->adpcmx /*adpcmm */ * DELTAT->volume; } #else /* delta step of re-sampling */ DELTAT->sample_step = (DELTAT->next_leveling - now_leveling) * DELTAT->volume_w_step; /* output of start point */ DELTAT->adpcml = now_leveling * DELTAT->volume; /* adjust to now */ DELTAT->adpcml += (int) ((double) DELTAT->sample_step * ((double) DELTAT->now_step / (double) DELTAT->step)); } DELTAT->adpcml += DELTAT->sample_step; #endif /* output for work of output channels (outd[OPNxxxx]) */ *(DELTAT->pan) += DELTAT->adpcml; } #endif /* YM_INLINE_BLOCK */