📄 ym3812.c
字号:
return cFALSE; /* Bad timer*/}/*$DEADSERIOUSCLAN$********************************************************************** ROUTINE* ym3812_KeyOn( ym3812 *pOPL, nChannel )* FUNCTION* Set Key On State for both slots related to this channel* NOTES****************************************************************************************/void ym3812_KeyOn( ym3812 *pOPL, int nChannel ){ int nSlot; if( !pOPL->fKeyDown[nChannel] ) { pOPL->fKeyDown[nChannel]=cTRUE; for( nSlot=nChannel*2; nSlot<nChannel*2+2; nSlot++ ) { pOPL->vEnvTime[nSlot] = 0.0f; pOPL->nEnvState[nSlot] = ADSR_Attack; } }}/*$DEADSERIOUSCLAN$********************************************************************** ROUTINE* ym3812_KeyOff( ym3812 *pOPL, int nChannel )* FUNCTION* NOTES****************************************************************************************/void ym3812_KeyOff( ym3812 *pOPL, int nChannel ){ pOPL->fKeyDown[nChannel] = cFALSE;}/*$DEADSERIOUS$******************************************************************** ROUTINE* int ym3812_ReadStatus( ym3812 *pOPL )* AUTHOR* Carl-Henrik Skarstedt* FUNCTION/NOTES* Returns the status of interrupts and timer overflow flags.**********************************************************************************/int ym3812_ReadStatus( ym3812 *pOPL ){ return pOPL->nStatus;}/*$DEADSERIOUS$******************************************************************** ROUTINE* int ym3812_ReadReg( ym3812 *pOPL )* AUTHOR* Carl-Henrik Skarstedt* FUNCTION/NOTES* Reads back register contents.**********************************************************************************/int ym3812_ReadReg( ym3812 *pOPL ){ return pOPL->aRegArray[pOPL->nReg];}/*$DEADSERIOUSCLAN$********************************************************************** ROUTINE* ym3812_SetReg( ym3812 *pOPL, unsigned char nReg )* FUNCTION* NOTES****************************************************************************************/void ym3812_SetReg( ym3812 *pOPL, unsigned char nReg ){ pOPL->nReg = nReg;}/*$DEADSERIOUSCLAN$********************************************************************** ROUTINE* ym3812_WriteReg( ym3812 *pOPL, unsigned char nData)* FUNCTION* NOTES****************************************************************************************/void ym3812_WriteReg( ym3812 *pOPL, unsigned char nData){ int nReg, nCh, nSlot, nTemp; nReg = pOPL->nReg; pOPL->aRegArray[nReg] = nData; switch( nReg ) { case 0x01: /* TEST - bit 5 means ym3812 mode on, not means ym3526 full compatibility*/ pOPL->fWave = nData & 0x20; return; case 0x02: /* DATA OF TIMER 1*/ pOPL->vTimer1 = TIME_IN_USEC ((256-nData) * 80) * ( (double)ym3812_StdClock / (double)pOPL->nYM3812Clk ); return; case 0x03: /* DATA OF TIMER 2*/ pOPL->vTimer2 = TIME_IN_USEC ((256-nData) * 320) * ( (double)ym3812_StdClock / (double)pOPL->nYM3812Clk ); return; case 0x04: /* IRQ-RESET/CONTROL OF TIMER 1 AND 2*/ if( nData & ym3812_TCIRQRES ) { nData &= ~ym3812_TCIRQRES; pOPL->nStatus &= ~(ym3812_STIRQ|ym3812_STFLAG1|ym3812_STFLAG2); } pOPL->nStatus &= ~(nData & 0x60); nTemp = pOPL->nTimerCtrl; pOPL->nTimerCtrl = nData; if( (nData&(~nTemp)) & ym3812_TCST1 ) { /* Start new timer if START TIMER1 was set this write.*/ if(pOPL->SetTimer!=NULL) (*pOPL->SetTimer)( 1, pOPL->vTimer1, pOPL, cFALSE ); } else if( ((~nData)&nTemp) & ym3812_TCST1 ) { /* Remove existing timer if START TIMER1 was cleared this write.*/ if(pOPL->SetTimer!=NULL) (*pOPL->SetTimer)( 1, pOPL->vTimer1, pOPL, cTRUE ); } if( (nData&(~nTemp)) & ym3812_TCST2 ) { /* Start new timer if START TIMER2 was set this write.*/ if(pOPL->SetTimer!=NULL) (*pOPL->SetTimer)( 2, pOPL->vTimer2, pOPL, cFALSE ); } else if( ((~nData)&nTemp) & ym3812_TCST2 ) { /* Remove existing timer if START TIMER2 was cleared this write.*/ if(pOPL->SetTimer!=NULL) (*pOPL->SetTimer)( 2, pOPL->vTimer2, pOPL, cTRUE ); } return; case 0x08: /* CSM SPEECH SYNTHESIS MODE/NOTE SELECT*/ pOPL->nCSM = nData; return; case 0xbd: /* DEPTH(AM/VIB)/RHYTHM(BD, SD, TOM, TC, HH)*/ nTemp = pOPL->nDepthRhythm; pOPL->nDepthRhythm=nData; /* 0xbd - control of depth and rhythm*/ nData &= ~nTemp; if( nData&0x10 ) pOPL->nDrumOffs[0] = 0; /* Bass Drum*/ if( nData&0x08 ) pOPL->nDrumOffs[1] = 0; /* Snare Drum*/ if( nData&0x04 ) pOPL->nDrumOffs[2] = 0; /* Tom tom*/ if( nData&0x02 ) pOPL->nDrumOffs[3] = 0; /* Top cymbal*/ if( nData&0x01 ) pOPL->nDrumOffs[4] = 0; /* Hihat*/ return; } nCh = nReg & 0x0f; if( nCh < 9 ) { switch( nReg & 0xf0 ) { case 0xa0: /* F-NUMBER (LOW 8 BITS) a0-a8*/ pOPL->nFNumber[nCh] = (pOPL->nFNumber[nCh] & 0x300) + (nData&0xff); return; case 0xb0: /* KEY-ON/BLOCK/F-NUMER(LOW 2 BITS) b0-b8*/ pOPL->nFNumber[nCh] = (pOPL->nFNumber[nCh] & 0x0ff) + ((nData&0x3)<<8); pOPL->nOctave[nCh] = (nData&0x1c)>>2; if( nData & 0x20 ) { ym3812_KeyOn( pOPL, nCh ); } else { ym3812_KeyOff( pOPL, nCh ); } return; case 0xc0: /* FEEDBACK/CONNECTION*/ pOPL->fConnection[nCh] = nData & 0x01; nData >>= 1; if( !nData ) /* Calculate Feedback RATIO!*/ { pOPL->nFeedback[nCh] = 0; } else { nData &= 7; pOPL->nFeedback[nCh] = SINTABLE_SHIFT-SINTABLE_SIZESHIFT+7-nData+14; } return; } } nSlot = RegSlot_Relation[nReg&0x1f]; if( nSlot != -1 ) { switch( nReg & 0xe0 ) { case 0x20: /* AM-MOD/VIBRATO/EG-TYP/KEY-SCALE-RATE/MULTIPLE*/ pOPL->fAM[nSlot] = nData & 0x80; pOPL->fVibrato[nSlot] = nData & 0x40; pOPL->fEGTyp[nSlot] = nData & 0x20; pOPL->fKSR[nSlot] = nData & 0x10; pOPL->nMulti[nSlot] = nData & 0x0f; return; case 0x40: /* KEY-SCALE LEVEL/TOTAL LEVEL*/ pOPL->nTotalLevel[nSlot] = nData&0x3f; pOPL->nKSL[nSlot] = (nData&0xc0)>>6; return; case 0x60: /* ATTACK RATE/DECAY RATE*/ if( nData&0xf0 ) pOPL->nAttack[nSlot] = nData >> 4; if( nData&0x0f ) pOPL->nDecay[nSlot] = nData & 0x0f; return; case 0x80: /* SYSTAIN LEVEL/RELEASE RATE*/ if( nData&0xf0 ) pOPL->nSustain[nSlot] = (nData >> 4)^0x0f; if( nData&0x0f ) pOPL->nRelease[nSlot] = nData & 0x0f; return; case 0xE0: /* WAVE SELECT*/ if( pOPL->fWave ) pOPL->nWave[nSlot] = nData & 0x03; else pOPL->nWave[nSlot] = 0; return; } } return; /* Writing to unused register - probably a bug*/}/*$DEADSERIOUS$********************************************* ROUTINE* void ym3812_Update_stream( int nChipID, void *pBuffer_in, int nLength )** FUNCTION* This routine plays streamed audio rather than a* set size buffer. Tailored for Mame streaming sounds.************************************************************/void ym3812_Update_stream( ym3812* pOPL, void *pBuffer_in, int nLength ){ int nCurrVol[18],nCurrAdd[18]; /* Volume for each slot, Frequency per channel*/ int fPlaying[9]; /* Is channel X playing??*/ int nVibMul,nAMAdd; /* Vibrato and Amplitude modulation numbers*/ int nRhythm,l,samp,Value,f16Bit; int nSlot,nSlot2,nOffs,nOffs2,nDiffAdd[5],nAdd[5]; int nSubDiv,nBufSize,nBufferLeft,nCh; float vMulLevel,vTime; /* Temporary max volume level in envelope generator*/ int nElapseTime; signed char *pRhythm[5]; char *pBuffer; short *pBuffer16; if( nLength == 0 ) return;/* 16bit or 8bit samples?*/ f16Bit = pOPL->f16Bit; pBuffer = pBuffer_in; pBuffer16 = (short*)pBuffer; nBufferLeft = nLength; if( pOPL->nReplayFrq == 0 ) return; nElapseTime = (nLength<<16) / pOPL->nReplayFrq; /* Time is in 16:16 format in seconds.*//* Do the subdivision stuff*/ for( nSubDiv=0; nSubDiv<pOPL->nSubDivide; nSubDiv++ ) { nBufSize = (nLength / pOPL->nSubDivide) + 1; nBufferLeft -= nBufSize; if (nBufferLeft < 0) nBufSize += nBufferLeft;/* Check how many of the Rhythm sounds are playing..*/ nRhythm = 0; for( l=0; l<5; l++ ) { if( pOPL->nDrumOffs[l]>=0 ) { if( (pOPL->pDrum[l]!=NULL)&&(pOPL->nDrumOffs[l]<pOPL->nDrumSize[l]) ) { pRhythm[nRhythm] = pOPL->pDrum[l]+pOPL->nDrumOffs[l]; pOPL->nDrumOffs[l] += (pOPL->nDrumRate[l] * nElapseTime) >> 16; nDiffAdd[nRhythm] = (pOPL->nDrumRate[l]<<8)/(pOPL->nReplayFrq); nAdd[nRhythm] = nDiffAdd[nRhythm]>>1; nRhythm += 1; } else { pOPL->nDrumOffs[l] = -1; } } }/* Get values from and then update Vibrato and AMDepth sinus offsets.*/ pOPL->nVibratoOffs += (int)(SINTABLE_SIZE * 6.4 * pOPL->nYM3812Clk / ym3812_StdClock / pOPL->nSubDivide * nElapseTime)>>16; /* Vibrato @ 6.4 Hz (3.6 MHz OPL)*/ pOPL->nAMDepthOffs += (int)(SINTABLE_SIZE * 3.7 * pOPL->nYM3812Clk / ym3812_StdClock / pOPL->nSubDivide * nElapseTime)>>16; /* AMDepth @ 3.7 Hz (3.6 MHz OPL)*/ nVibMul = (int)(ym3812_aSinTable[0][pOPL->nVibratoOffs] * 0.007f); if( pOPL->nDepthRhythm & 0x40 ) nVibMul *= 2; if( pOPL->nDepthRhythm & 0x80 ) { /* 4.8 dB max Amplitude Modulation*/ nAMAdd = (int)(ym3812_aSinTable[0][pOPL->nAMDepthOffs] * 4.8 / (47.25*(SINTABLE_MAX/256))); } else { /* 1.0 dB max Amplitude Modulation*/ nAMAdd = (int)(ym3812_aSinTable[0][pOPL->nAMDepthOffs] / (47.25*(SINTABLE_MAX/256))); } pOPL->nVibratoOffs &= SINTABLE_SIZE-1; pOPL->nAMDepthOffs &= SINTABLE_SIZE-1;/* Calculate frequency for each slot*/ for( l=0; l<18; l++ ) { nCurrAdd[l] = ( ym3812_Multi[pOPL->nMulti[l]] * pOPL->nFNumber[l>>1] * pOPL->nYM3812DivClk / pOPL->nReplayFrq ) << ( (SINTABLE_SIZESHIFT-20+7)+pOPL->nOctave[l>>1] ); if( pOPL->fVibrato[l] ) nCurrAdd[l] += (nCurrAdd[l]*nVibMul)>>SINTABLE_SHIFT; }/* Update currently playing envelopes (one per slot)*/ for( l=0; l<18 ; l++ ) { if( (l&1)==0 ) fPlaying[l>>1] = cFALSE; if( pOPL->nEnvState[l] == ADSR_Silent ) { nCurrVol[l] = 0; } else { nCh = l>>1; vMulLevel = (float)(0x3f-pOPL->nTotalLevel[l]); /* Emulate KSL*/ switch( pOPL->nKSL[l] ) { case 3: vMulLevel += pOPL->nOctave[nCh]*(6.0f*64.0f/47.5f); break; case 2: vMulLevel += pOPL->nOctave[nCh]*(1.5f*64.0f/47.5f); break; case 1: vMulLevel += pOPL->nOctave[nCh]*(3.0f*64.0f/47.5f); break; } /* Emulate amplitude modulation*/ if( pOPL->fAM[l] ) vMulLevel += nAMAdd; vMulLevel *= 4.0f; /* Original volume 0-64 my volume 0-256*/ if( vMulLevel>255.0f ) vMulLevel = 255.0f; if( vMulLevel<0.0f ) vMulLevel = 0.0f; switch( pOPL->nEnvState[l] ) { case ADSR_Attack: vTime = ym3812_aAttackTime[pOPL->nAttack[l]]; if( pOPL->vEnvTime[l] < vTime ) { /* The attack is actually linear, so this is not a typo.*/ nCurrVol[l] = (int)(pOPL->aVolumes[(int)vMulLevel] * pOPL->vEnvTime[l] / vTime); break; /* Do not fall through to Decay*/ } else { pOPL->vEnvTime[l] -= vTime; pOPL->nEnvState[l] = ADSR_Decay; } /* Fall through to Decay..*/ case ADSR_Decay: if( (!pOPL->fEGTyp[l])||(pOPL->fKeyDown[nCh]) ) { vTime = ym3812_aDecayTime[pOPL->nDecay[l]]; if( pOPL->vEnvTime[l] < vTime ) { nCurrVol[l] = pOPL->aVolumes[(int)( vMulLevel * ((1.0f - pOPL->vEnvTime[l]/vTime) * (15 - pOPL->nSustain[l]) + (pOPL->nSustain[l])))>>4]; break; /* Do not fall through to Sustain/Release*/ } else { pOPL->vEnvTime[l] -= vTime; pOPL->nEnvState[l] = ADSR_Sustain; } } /* Fall through to Sustain/Release..*/ case ADSR_Sustain: if( (pOPL->fEGTyp[l])&&(pOPL->fKeyDown[nCh]) ) { /* Wait forever (until KeyOff)*/ nCurrVol[l] = pOPL->aVolumes[(int)(vMulLevel*pOPL->nSustain[l])>>4]; break; /* No fall through to release*/ } else { /* No hold state in Sustain (Ignore KeyOff)*/ pOPL->vEnvTime[l] = 0.0f; pOPL->nEnvState[l] = ADSR_Release; } case ADSR_Release: vTime = ym3812_aDecayTime[pOPL->nRelease[l]]; /* Release timing = decay timing*/ if( pOPL->vEnvTime[l] < vTime ) { nCurrVol[l] = pOPL->aVolumes[(int)(vMulLevel * (1.0f-pOPL->vEnvTime[l]/vTime)*pOPL->nSustain[l])>>4]; break; } else { /* Envelop has played and died*/ nCurrVol[l] = 0; pOPL->vEnvTime[l] = 0.0f; /* This Correct???*/ pOPL->nEnvState[l] = ADSR_Silent; break; } } /* Update envelop time for _NEXT_ frame*/ pOPL->vEnvTime[l] += (float)nElapseTime/65536.0f/(float)pOPL->nSubDivide; if( nCurrVol[l]>0 ) fPlaying[nCh] = cTRUE; } } /* Generate sample buffer*/ for( samp=nBufSize; samp>=0; --samp ) { /* Calculate one sample from all active FM channels*/ Value = 0; for( l=8; l>=0; --l ) /* Do all channels*/ { if( fPlaying[l] ) /* Is this channel playing at all?*/ { nSlot = l<<1; /* Set Slot A*/ nSlot2 = nSlot+1; /* Set Slot B*/ nOffs = ( pOPL->nCurrPos[nSlot] >> 8); /* Calculate sintable offset this frame Slot A*/ nOffs2 = ( pOPL->nCurrPos[nSlot2] >> 8); /* Calculate sintable offset this frame Slot B*/ if( pOPL->nFeedback[l]!=0 ) /* Fix feedback*/ { nOffs += (pOPL->nSinValue[nSlot])>>(pOPL->nFeedback[l]);/* Add feedback offset*/ } nOffs &= SINTABLE_SIZE-1; pOPL->nSinValue[nSlot] = nCurrVol[nSlot]*ym3812_aSinTable[pOPL->nWave[nSlot]][nOffs]; /* Set old sample value*/ if( pOPL->fConnection[l] ) /* Connect or Parallell?*/ { Value += nCurrVol[nSlot] * ym3812_aSinTable[pOPL->nWave[nSlot]][nOffs] + nCurrVol[nSlot2] * ym3812_aSinTable[pOPL->nWave[nSlot2]][nOffs2&(SINTABLE_SIZE-1)]; } else { Value += nCurrVol[nSlot2]*ym3812_aSinTable[pOPL->nWave[nSlot2]][(nOffs2 + ((nCurrVol[nSlot]*ym3812_aSinTable[pOPL->nWave[nSlot]][nOffs])>>(14))) & (SINTABLE_SIZE-1)]; } pOPL->nCurrPos[nSlot] += nCurrAdd[nSlot]; /* Increase sintable offset Slot A*/ pOPL->nCurrPos[nSlot2] += nCurrAdd[nSlot2]; /* Increase sintable offset Slot B*/ } } /* Calculate one sample from all active Rhythm sounds*/ for( l=0; l<nRhythm; l++ ) { Value += (*pRhythm[l] * ym3812_StdVolume)<<(SINTABLE_SHIFT); pRhythm[l] += nAdd[l]>>8; nAdd[l] = (nAdd[l]&0xff)+nDiffAdd[l]; } /* Put sample value into sample buffer*/ if( f16Bit ) *pBuffer16++ = ym3812_Sign16(Value >> ( SINTABLE_SHIFT+1 )); else *pBuffer++ = ym3812_Sign8(Value >> ( SINTABLE_SHIFT+1+8 )); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -