// WRITE REGISTERS: called by main emu void SPU_writeRegister(unsigned long reg, unsigned short val) { const unsigned long r=reg&0xfff; regArea[(r-0xc00)>>1] = val; if(r>=0x0c00 && r<0x0d80) // some channel info? { int ch=(r>>4)-0xc0; // calc channel switch(r&0x0f) { case 0: SetVolumeL((unsigned char)ch,val); // l volume break; case 2: SetVolumeR((unsigned char)ch,val); // r volume break; case 4: SetPitch(ch,val); // pitch break; case 6: s_chan[ch].pStart=spuMemC+((unsigned long) val<<3); // start break; case 8: // level with pre-calcs { const unsigned long lval=val;unsigned long lx; s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; s_chan[ch].ADSRX.AttackRate = ((lval>>8) & 0x007f)^0x7f; s_chan[ch].ADSRX.DecayRate = 4*(((lval>>4) & 0x000f)^0x1f); s_chan[ch].ADSRX.SustainLevel = (lval & 0x000f) << 27; break; } case 10: // adsr times with pre-calcs { const unsigned long lval=val;unsigned long lx; s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0; s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1; s_chan[ch].ADSRX.SustainRate = ((lval>>6) & 0x007f)^0x7f; s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0; s_chan[ch].ADSRX.ReleaseRate = 4*((lval & 0x001f)^0x1f); break; } case 12: // adsr volume... mmm have to investigate this break; case 14: // loop? s_chan[ch].pLoop=spuMemC+((unsigned long) val<<3); s_chan[ch].bIgnoreLoop=1; break; } return; }
EXPORT_GCC void CALLBACK SPU2write(unsigned long reg, unsigned short val) { long r=reg&0xffff; regArea[r>>1] = val; // printf("SPU2: %04x to %08x\n", val, reg); if((r>=0x0000 && r<0x0180)||(r>=0x0400 && r<0x0580)) // some channel info? { int ch=(r>>4)&0x1f; if(r>=0x400) ch+=24; switch(r&0x0f) { //------------------------------------------------// r volume case 0: SetVolumeL((unsigned char)ch,val); break; //------------------------------------------------// l volume case 2: SetVolumeR((unsigned char)ch,val); break; //------------------------------------------------// pitch case 4: SetPitch(ch,val); break; //------------------------------------------------// level with pre-calcs case 6: { const unsigned long lval=val;unsigned long lx; //---------------------------------------------// s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f; s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f; s_chan[ch].ADSRX.SustainLevel=lval & 0x000f; //---------------------------------------------// if(!iDebugMode) break; //---------------------------------------------// stuff below is only for debug mode s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0; //0x007f lx=(((lval>>8) & 0x007f)>>2); // attack time to run from 0 to 100% volume lx = (lx < 31) ? lx : 31; // no overflow on shift! if(lx) { lx = (1<<lx); if(lx<2147483) lx=(lx*ATTACK_MS)/10000L; // another overflow check else lx=(lx/10000L)*ATTACK_MS; if(!lx) lx=1; } s_chan[ch].ADSR.AttackTime=lx; s_chan[ch].ADSR.SustainLevel= // our adsr vol runs from 0 to 1024, so scale the sustain level (1024*((lval) & 0x000f))/15; lx=(lval>>4) & 0x000f; // decay: if(lx) // our const decay value is time it takes from 100% to 0% of volume { lx = ((1<<(lx))*DECAY_MS)/10000L; if(!lx) lx=1; } s_chan[ch].ADSR.DecayTime = // so calc how long does it take to run from 100% to the wanted sus level (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024; } break; //------------------------------------------------// adsr times with pre-calcs case 8: { const unsigned long lval=val;unsigned long lx; //----------------------------------------------// s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0; s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1; s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f; s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0; s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f; //----------------------------------------------// if(!iDebugMode) break; //----------------------------------------------// stuff below is only for debug mode s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0; s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0; lx=((((lval>>6) & 0x007f)>>2)); // sustain time... often very high lx = (lx < 31) ? lx : 31; // values are used to hold the volume if(lx) // until a sound stop occurs { // the highest value we reach (due to lx = (1<<lx); // overflow checking) is: if(lx<2147483) lx=(lx*SUSTAIN_MS)/10000L; // 94704 seconds = 1578 minutes = 26 hours... else lx=(lx/10000L)*SUSTAIN_MS; // should be enuff... if the stop doesn't if(!lx) lx=1; // come in this time span, I don't care :) } s_chan[ch].ADSR.SustainTime = lx; lx=(lval & 0x001f); s_chan[ch].ADSR.ReleaseVal =lx; if(lx) // release time from 100% to 0% { // note: the release time will be lx = (1<<lx); // adjusted when a stop is coming, if(lx<2147483) lx=(lx*RELEASE_MS)/10000L; // so at this time the adsr vol will else lx=(lx/10000L)*RELEASE_MS; // run from (current volume) to 0% if(!lx) lx=1; } s_chan[ch].ADSR.ReleaseTime=lx; if(lval & 0x4000) // add/dec flag s_chan[ch].ADSR.SustainModeDec=-1; else s_chan[ch].ADSR.SustainModeDec=1; } break; //------------------------------------------------// } iSpuAsyncWait=0; return; }
void CALLBACK SPUsetVolumeR(unsigned char ch, short vol) { SetVolumeL(ch,vol); }
void CALLBACK SPUwriteRegister(unsigned long reg, unsigned short val) { const unsigned long r=reg&0xfff; regArea[(r-0xc00)>>1] = val; if(r>=0x0c00 && r<0x0d80) // some channel info? { int ch=(r>>4)-0xc0; // calc channel switch(r&0x0f) { //------------------------------------------------// r volume case 0: SetVolumeL((unsigned char)ch,val); break; //------------------------------------------------// l volume case 2: SetVolumeR((unsigned char)ch,val); break; //------------------------------------------------// pitch case 4: SetPitch(ch,val); break; //------------------------------------------------// start case 6: s_chan[ch].pStart=spuMemC+((unsigned long) val<<3); break; //------------------------------------------------// level with pre-calcs case 8: { const unsigned long lval=val;unsigned long lx; //---------------------------------------------// s_chan[ch].ADSRX.AttackModeExp=(lval&0x8000)?1:0; s_chan[ch].ADSRX.AttackRate=(lval>>8) & 0x007f; s_chan[ch].ADSRX.DecayRate=(lval>>4) & 0x000f; s_chan[ch].ADSRX.SustainLevel=lval & 0x000f; //---------------------------------------------// if(!iDebugMode) break; //---------------------------------------------// stuff below is only for debug mode s_chan[ch].ADSR.AttackModeExp=(lval&0x8000)?1:0; //0x007f lx=(((lval>>8) & 0x007f)>>2); // attack time to run from 0 to 100% volume lx=min(31,lx); // no overflow on shift! if(lx) { lx = (1<<lx); if(lx<2147483) lx=(lx*ATTACK_MS)/10000L; // another overflow check else lx=(lx/10000L)*ATTACK_MS; if(!lx) lx=1; } s_chan[ch].ADSR.AttackTime=lx; s_chan[ch].ADSR.SustainLevel= // our adsr vol runs from 0 to 1024, so scale the sustain level (1024*((lval) & 0x000f))/15; lx=(lval>>4) & 0x000f; // decay: if(lx) // our const decay value is time it takes from 100% to 0% of volume { lx = ((1<<(lx))*DECAY_MS)/10000L; if(!lx) lx=1; } s_chan[ch].ADSR.DecayTime = // so calc how long does it take to run from 100% to the wanted sus level (lx*(1024-s_chan[ch].ADSR.SustainLevel))/1024; } break; //------------------------------------------------// adsr times with pre-calcs case 10: { const unsigned long lval=val;unsigned long lx; //----------------------------------------------// s_chan[ch].ADSRX.SustainModeExp = (lval&0x8000)?1:0; s_chan[ch].ADSRX.SustainIncrease= (lval&0x4000)?0:1; s_chan[ch].ADSRX.SustainRate = (lval>>6) & 0x007f; s_chan[ch].ADSRX.ReleaseModeExp = (lval&0x0020)?1:0; s_chan[ch].ADSRX.ReleaseRate = lval & 0x001f; //----------------------------------------------// if(!iDebugMode) break; //----------------------------------------------// stuff below is only for debug mode s_chan[ch].ADSR.SustainModeExp = (lval&0x8000)?1:0; s_chan[ch].ADSR.ReleaseModeExp = (lval&0x0020)?1:0; lx=((((lval>>6) & 0x007f)>>2)); // sustain time... often very high lx=min(31,lx); // values are used to hold the volume if(lx) // until a sound stop occurs { // the highest value we reach (due to lx = (1<<lx); // overflow checking) is: if(lx<2147483) lx=(lx*SUSTAIN_MS)/10000L; // 94704 seconds = 1578 minutes = 26 hours... else lx=(lx/10000L)*SUSTAIN_MS; // should be enuff... if the stop doesn't if(!lx) lx=1; // come in this time span, I don't care :) } s_chan[ch].ADSR.SustainTime = lx; lx=(lval & 0x001f); s_chan[ch].ADSR.ReleaseVal =lx; if(lx) // release time from 100% to 0% { // note: the release time will be lx = (1<<lx); // adjusted when a stop is coming, if(lx<2147483) lx=(lx*RELEASE_MS)/10000L; // so at this time the adsr vol will else lx=(lx/10000L)*RELEASE_MS; // run from (current volume) to 0% if(!lx) lx=1; } s_chan[ch].ADSR.ReleaseTime=lx; if(lval & 0x4000) // add/dec flag s_chan[ch].ADSR.SustainModeDec=-1; else s_chan[ch].ADSR.SustainModeDec=1; } break; //------------------------------------------------// adsr volume... mmm have to investigate this case 12: break; //------------------------------------------------// case 14: // loop? //WaitForSingleObject(s_chan[ch].hMutex,2000); // -> no multithread fuckups s_chan[ch].pLoop=spuMemC+((unsigned long) val<<3); s_chan[ch].bIgnoreLoop=1; //ReleaseMutex(s_chan[ch].hMutex); // -> oki, on with the thread break; //------------------------------------------------// } iSpuAsyncWait=0; return; }