diff options
author | Jeremy Simon <jsimon13@yahoo.fr> | 2002-02-28 21:10:42 +0000 |
---|---|---|
committer | Jeremy Simon <jsimon13@yahoo.fr> | 2002-02-28 21:10:42 +0000 |
commit | ac87bfc370ec15c9c81f8738659fb2582b14b792 (patch) | |
tree | 2d2cd21cfc4faf885b5bf19b9c2f1f78add5c013 /gst/modplug/libmodplug/snd_fx.cpp | |
parent | 3b68b42af8feb78fe37cfadb6624e483f2e11d7d (diff) | |
download | gst-plugins-bad-ac87bfc370ec15c9c81f8738659fb2582b14b792.tar.gz gst-plugins-bad-ac87bfc370ec15c9c81f8738659fb2582b14b792.tar.bz2 gst-plugins-bad-ac87bfc370ec15c9c81f8738659fb2582b14b792.zip |
adding modplug
Original commit message from CVS:
adding modplug
Diffstat (limited to 'gst/modplug/libmodplug/snd_fx.cpp')
-rw-r--r-- | gst/modplug/libmodplug/snd_fx.cpp | 2391 |
1 files changed, 2391 insertions, 0 deletions
diff --git a/gst/modplug/libmodplug/snd_fx.cpp b/gst/modplug/libmodplug/snd_fx.cpp new file mode 100644 index 00000000..78722d5a --- /dev/null +++ b/gst/modplug/libmodplug/snd_fx.cpp @@ -0,0 +1,2391 @@ +/* + * This source code is public domain. + * + * Authors: Olivier Lapicque <olivierl@jps.net> +*/ + +#include "stdafx.h" +#include "sndfile.h" + +#ifdef MSC_VER +#pragma warning(disable:4244) +#endif + +// Tables defined in tables.cpp +extern BYTE ImpulseTrackerPortaVolCmd[16]; +extern WORD S3MFineTuneTable[16]; +extern WORD ProTrackerPeriodTable[6*12]; +extern WORD ProTrackerTunedPeriods[15*12]; +extern WORD FreqS3MTable[]; +extern WORD XMPeriodTable[96+8]; +extern UINT XMLinearTable[768]; +extern DWORD FineLinearSlideUpTable[16]; +extern DWORD FineLinearSlideDownTable[16]; +extern DWORD LinearSlideUpTable[256]; +extern DWORD LinearSlideDownTable[256]; +extern signed char retrigTable1[16]; +extern signed char retrigTable2[16]; +extern short int ModRandomTable[64]; + + +//////////////////////////////////////////////////////////// +// Length + +DWORD CSoundFile::GetLength(BOOL bAdjust, BOOL bTotal) +//---------------------------------------------------- +{ + UINT dwElapsedTime=0, nRow=0, nCurrentPattern=0, nNextPattern=0, nPattern=Order[0]; + UINT nMusicSpeed=m_nDefaultSpeed, nMusicTempo=m_nDefaultTempo, nNextRow=0; + UINT nMaxRow = 0, nMaxPattern = 0; + LONG nGlbVol = m_nDefaultGlobalVolume, nOldGlbVolSlide = 0; + BYTE samples[MAX_CHANNELS]; + BYTE instr[MAX_CHANNELS]; + BYTE notes[MAX_CHANNELS]; + BYTE vols[MAX_CHANNELS]; + BYTE oldparam[MAX_CHANNELS]; + BYTE chnvols[MAX_CHANNELS]; + DWORD patloop[MAX_CHANNELS]; + + memset(instr, 0, sizeof(instr)); + memset(notes, 0, sizeof(notes)); + memset(vols, 0xFF, sizeof(vols)); + memset(patloop, 0, sizeof(patloop)); + memset(oldparam, 0, sizeof(oldparam)); + memset(chnvols, 64, sizeof(chnvols)); + memset(samples, 0, sizeof(samples)); + for (UINT icv=0; icv<m_nChannels; icv++) chnvols[icv] = ChnSettings[icv].nVolume; + nMaxRow = m_nNextRow; + nMaxPattern = m_nNextPattern; + nCurrentPattern = nNextPattern = 0; + nPattern = Order[0]; + nRow = nNextRow = 0; + for (;;) + { + UINT nSpeedCount = 0; + nRow = nNextRow; + nCurrentPattern = nNextPattern; + // Check if pattern is valid + nPattern = Order[nCurrentPattern]; + while (nPattern >= MAX_PATTERNS) + { + // End of song ? + if ((nPattern == 0xFF) || (nCurrentPattern >= MAX_ORDERS)) + { + goto EndMod; + } else + { + nCurrentPattern++; + nPattern = (nCurrentPattern < MAX_ORDERS) ? Order[nCurrentPattern] : 0xFF; + } + nNextPattern = nCurrentPattern; + } + // Weird stuff? + if ((nPattern >= MAX_PATTERNS) || (!Patterns[nPattern])) break; + // Should never happen + if (nRow >= PatternSize[nPattern]) nRow = 0; + // Update next position + nNextRow = nRow + 1; + if (nNextRow >= PatternSize[nPattern]) + { + nNextPattern = nCurrentPattern + 1; + nNextRow = 0; + } + if (!nRow) + { + for (UINT ipck=0; ipck<m_nChannels; ipck++) patloop[ipck] = dwElapsedTime; + } + if (!bTotal) + { + if ((nCurrentPattern > nMaxPattern) || ((nCurrentPattern == nMaxPattern) && (nRow >= nMaxRow))) + { + if (bAdjust) + { + m_nMusicSpeed = nMusicSpeed; + m_nMusicTempo = nMusicTempo; + } + break; + } + } + MODCHANNEL *pChn = Chn; + MODCOMMAND *p = Patterns[nPattern] + nRow * m_nChannels; + for (UINT nChn=0; nChn<m_nChannels; p++,pChn++, nChn++) if (*((DWORD *)p)) + { + UINT command = p->command; + UINT param = p->param; + UINT note = p->note; + if (p->instr) { instr[nChn] = p->instr; notes[nChn] = 0; vols[nChn] = 0xFF; } + if ((note) && (note <= 120)) notes[nChn] = note; + if (p->volcmd == VOLCMD_VOLUME) { vols[nChn] = p->vol; } + if (command) switch (command) + { + // Position Jump + case CMD_POSITIONJUMP: + if (param <= nCurrentPattern) goto EndMod; + nNextPattern = param; + nNextRow = 0; + if (bAdjust) + { + pChn->nPatternLoopCount = 0; + pChn->nPatternLoop = 0; + } + break; + // Pattern Break + case CMD_PATTERNBREAK: + nNextRow = param; + nNextPattern = nCurrentPattern + 1; + if (bAdjust) + { + pChn->nPatternLoopCount = 0; + pChn->nPatternLoop = 0; + } + break; + // Set Speed + case CMD_SPEED: + if (!param) break; + if ((param <= 0x20) || (m_nType != MOD_TYPE_MOD)) + { + if (param < 128) nMusicSpeed = param; + } + break; + // Set Tempo + case CMD_TEMPO: + if ((bAdjust) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) + { + if (param) pChn->nOldTempo = param; else param = pChn->nOldTempo; + } + if (param >= 0x20) nMusicTempo = param; else + // Tempo Slide + if ((param & 0xF0) == 0x10) + { + nMusicTempo += param & 0x0F; + if (nMusicTempo > 255) nMusicTempo = 255; + } else + { + nMusicTempo -= param & 0x0F; + if (nMusicTempo < 32) nMusicTempo = 32; + } + break; + // Pattern Delay + case CMD_S3MCMDEX: + if ((param & 0xF0) == 0x60) { nSpeedCount = param & 0x0F; break; } else + if ((param & 0xF0) == 0xB0) { param &= 0x0F; param |= 0x60; } + case CMD_MODCMDEX: + if ((param & 0xF0) == 0xE0) nSpeedCount = (param & 0x0F) * nMusicSpeed; else + if ((param & 0xF0) == 0x60) + { + if (param & 0x0F) dwElapsedTime += (dwElapsedTime - patloop[nChn]) * (param & 0x0F); + else patloop[nChn] = dwElapsedTime; + } + break; + } + if (!bAdjust) continue; + switch(command) + { + // Portamento Up/Down + case CMD_PORTAMENTOUP: + case CMD_PORTAMENTODOWN: + if (param) pChn->nOldPortaUpDown = param; + break; + // Tone-Portamento + case CMD_TONEPORTAMENTO: + if (param) pChn->nPortamentoSlide = param << 2; + break; + // Offset + case CMD_OFFSET: + if (param) pChn->nOldOffset = param; + break; + // Volume Slide + case CMD_VOLUMESLIDE: + case CMD_TONEPORTAVOL: + case CMD_VIBRATOVOL: + if (param) pChn->nOldVolumeSlide = param; + break; + // Set Volume + case CMD_VOLUME: + vols[nChn] = param; + break; + // Global Volume + case CMD_GLOBALVOLUME: + if (m_nType != MOD_TYPE_IT) param <<= 1; + if (param > 128) param = 128; + nGlbVol = param << 1; + break; + // Global Volume Slide + case CMD_GLOBALVOLSLIDE: + if (param) nOldGlbVolSlide = param; else param = nOldGlbVolSlide; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + param >>= 4; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol += param << 1; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + param = (param & 0x0F) << 1; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol -= param; + } else + if (param & 0xF0) + { + param >>= 4; + param <<= 1; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol += param * nMusicSpeed; + } else + { + param = (param & 0x0F) << 1; + if (m_nType != MOD_TYPE_IT) param <<= 1; + nGlbVol -= param * nMusicSpeed; + } + if (nGlbVol < 0) nGlbVol = 0; + if (nGlbVol > 256) nGlbVol = 256; + break; + case CMD_CHANNELVOLUME: + if (param <= 64) chnvols[nChn] = param; + break; + case CMD_CHANNELVOLSLIDE: + if (param) oldparam[nChn] = param; else param = oldparam[nChn]; + pChn->nOldChnVolSlide = param; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + param = (param >> 4) + chnvols[nChn]; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (chnvols[nChn] > (int)(param & 0x0F)) param = chnvols[nChn] - (param & 0x0F); + else param = 0; + } else + if (param & 0x0F) + { + param = (param & 0x0F) * nMusicSpeed; + param = (chnvols[nChn] > param) ? chnvols[nChn] - param : 0; + } else param = ((param & 0xF0) >> 4) * nMusicSpeed + chnvols[nChn]; + if (param > 64) param = 64; + chnvols[nChn] = param; + break; + } + } + nSpeedCount += nMusicSpeed; + dwElapsedTime += (2500 * nSpeedCount) / nMusicTempo; + } +EndMod: + if ((bAdjust) && (!bTotal)) + { + m_nGlobalVolume = nGlbVol; + m_nOldGlbVolSlide = nOldGlbVolSlide; + for (UINT n=0; n<m_nChannels; n++) + { + Chn[n].nGlobalVol = chnvols[n]; + if (notes[n]) Chn[n].nNewNote = notes[n]; + if (instr[n]) Chn[n].nNewIns = instr[n]; + if (vols[n] != 0xFF) + { + if (vols[n] > 64) vols[n] = 64; + Chn[n].nVolume = vols[n] << 2; + } + } + } + return (dwElapsedTime+500) / 1000; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Effects + +void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta, BOOL bUpdVol, BOOL bResetEnv) +//-------------------------------------------------------------------------------------------------------- +{ + BOOL bInstrumentChanged = FALSE; + + if (instr >= MAX_INSTRUMENTS) return; + INSTRUMENTHEADER *penv = Headers[instr]; + MODINSTRUMENT *psmp = &Ins[instr]; + UINT note = pChn->nNewNote; + if ((penv) && (note) && (note <= 128)) + { + if (penv->NoteMap[note-1] >= 0xFE) return; + UINT n = penv->Keyboard[note-1]; + psmp = ((n) && (n < MAX_SAMPLES)) ? &Ins[n] : NULL; + } else + if (m_nInstruments) + { + if (note >= 0xFE) return; + psmp = NULL; + } + // Update Volume + if (bUpdVol) pChn->nVolume = (psmp) ? psmp->nVolume : 0; + // bInstrumentChanged is used for IT carry-on env option + if (penv != pChn->pHeader) + { + bInstrumentChanged = TRUE; + pChn->pHeader = penv; + } else + // Special XM hack + if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (penv) + && (pChn->pInstrument) && (psmp != pChn->pInstrument)) + { + // FT2 doesn't change the sample in this case, + // but still uses the sample info from the old one (bug?) + return; + } + // Instrument adjust + pChn->nNewIns = 0; + if (psmp) + { + if (penv) + { + pChn->nInsVol = (psmp->nGlobalVol * penv->nGlobalVol) >> 6; + if (penv->dwFlags & ENV_SETPANNING) pChn->nPan = penv->nPan; + pChn->nNNA = penv->nNNA; + } else + { + pChn->nInsVol = psmp->nGlobalVol; + } + if (psmp->uFlags & CHN_PANNING) pChn->nPan = psmp->nPan; + } + // Reset envelopes + if (bResetEnv) + { + if ((!bPorta) || (!(m_nType & MOD_TYPE_IT)) || (m_dwSongFlags & SONG_ITCOMPATMODE) + || (!pChn->nLength) || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol))) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + if ((m_nType & MOD_TYPE_IT) && (!bInstrumentChanged) && (penv) && (!(pChn->dwFlags & (CHN_KEYOFF|CHN_NOTEFADE)))) + { + if (!(penv->dwFlags & ENV_VOLCARRY)) pChn->nVolEnvPosition = 0; + if (!(penv->dwFlags & ENV_PANCARRY)) pChn->nPanEnvPosition = 0; + if (!(penv->dwFlags & ENV_PITCHCARRY)) pChn->nPitchEnvPosition = 0; + } else + { + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + } + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } else + if ((penv) && (!(penv->dwFlags & ENV_VOLUME))) + { + pChn->nVolEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } + } + // Invalid sample ? + if (!psmp) + { + pChn->pInstrument = NULL; + pChn->nInsVol = 0; + return; + } + // Tone-Portamento doesn't reset the pingpong direction flag + if ((bPorta) && (psmp == pChn->pInstrument)) + { + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) return; + pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE); + pChn->dwFlags = (pChn->dwFlags & (0xFFFFFF00 | CHN_PINGPONGFLAG)) | (psmp->uFlags); + } else + { + pChn->dwFlags &= ~(CHN_KEYOFF|CHN_NOTEFADE|CHN_VOLENV|CHN_PANENV|CHN_PITCHENV); + pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (psmp->uFlags); + if (penv) + { + if (penv->dwFlags & ENV_VOLUME) pChn->dwFlags |= CHN_VOLENV; + if (penv->dwFlags & ENV_PANNING) pChn->dwFlags |= CHN_PANENV; + if (penv->dwFlags & ENV_PITCH) pChn->dwFlags |= CHN_PITCHENV; + if ((penv->dwFlags & ENV_PITCH) && (penv->dwFlags & ENV_FILTER)) + { + if (!pChn->nCutOff) pChn->nCutOff = 0x7F; + } + if (penv->nIFC & 0x80) pChn->nCutOff = penv->nIFC & 0x7F; + if (penv->nIFR & 0x80) pChn->nResonance = penv->nIFR & 0x7F; + } + pChn->nVolSwing = pChn->nPanSwing = 0; + } + pChn->pInstrument = psmp; + pChn->nLength = psmp->nLength; + pChn->nLoopStart = psmp->nLoopStart; + pChn->nLoopEnd = psmp->nLoopEnd; + pChn->nC4Speed = psmp->nC4Speed; + pChn->pSample = psmp->pSample; + pChn->nTranspose = psmp->RelativeTone; + pChn->nFineTune = psmp->nFineTune; + if (pChn->dwFlags & CHN_SUSTAINLOOP) + { + pChn->nLoopStart = psmp->nSustainStart; + pChn->nLoopEnd = psmp->nSustainEnd; + pChn->dwFlags |= CHN_LOOP; + if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; + } + if ((pChn->dwFlags & CHN_LOOP) && (pChn->nLoopEnd < pChn->nLength)) pChn->nLength = pChn->nLoopEnd; +} + + +void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) +//--------------------------------------------------------------------------- +{ + if (note < 1) return; + MODCHANNEL * const pChn = &Chn[nChn]; + MODINSTRUMENT *pins = pChn->pInstrument; + INSTRUMENTHEADER *penv = pChn->pHeader; + if ((penv) && (note <= 0x80)) + { + UINT n = penv->Keyboard[note - 1]; + if ((n) && (n < MAX_SAMPLES)) pins = &Ins[n]; + note = penv->NoteMap[note-1]; + } + // Key Off + if (note >= 0x80) // 0xFE or invalid note => key off + { + // Key Off + KeyOff(nChn); + // Note Cut + if (note == 0xFE) + { + pChn->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + if ((!(m_nType & MOD_TYPE_IT)) || (m_nInstruments)) pChn->nVolume = 0; + pChn->nFadeOutVol = 0; + } + return; + } + if (!pins) return; + if ((!bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MED|MOD_TYPE_MT2))) + { + pChn->nTranspose = pins->RelativeTone; + pChn->nFineTune = pins->nFineTune; + } + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED)) note += pChn->nTranspose; + if (note < 1) note = 1; + if (note > 132) note = 132; + pChn->nNote = note; + if ((!bPorta) || (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) pChn->nNewIns = 0; + UINT period = GetPeriodFromNote(note, pChn->nFineTune, pChn->nC4Speed); + if (period) + { + if ((!bPorta) || (!pChn->nPeriod)) pChn->nPeriod = period; + pChn->nPortamentoDest = period; + if ((!bPorta) || ((!pChn->nLength) && (!(m_nType & MOD_TYPE_S3M)))) + { + pChn->pInstrument = pins; + pChn->pSample = pins->pSample; + pChn->nLength = pins->nLength; + pChn->nLoopEnd = pins->nLength; + pChn->nLoopStart = 0; + pChn->dwFlags = (pChn->dwFlags & 0xFFFFFF00) | (pins->uFlags); + if (pChn->dwFlags & CHN_SUSTAINLOOP) + { + pChn->nLoopStart = pins->nSustainStart; + pChn->nLoopEnd = pins->nSustainEnd; + pChn->dwFlags &= ~CHN_PINGPONGLOOP; + pChn->dwFlags |= CHN_LOOP; + if (pChn->dwFlags & CHN_PINGPONGSUSTAIN) pChn->dwFlags |= CHN_PINGPONGLOOP; + if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + } else + if (pChn->dwFlags & CHN_LOOP) + { + pChn->nLoopStart = pins->nLoopStart; + pChn->nLoopEnd = pins->nLoopEnd; + if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + } + pChn->nPos = 0; + pChn->nPosLo = 0; + if (pChn->nVibratoType < 4) pChn->nVibratoPos = ((m_nType & MOD_TYPE_IT) && (!(m_dwSongFlags & SONG_ITOLDEFFECTS))) ? 0x10 : 0; + if (pChn->nTremoloType < 4) pChn->nTremoloPos = 0; + } + if (pChn->nPos >= pChn->nLength) pChn->nPos = pChn->nLoopStart; + } else bPorta = FALSE; + if ((!bPorta) || (!(m_nType & MOD_TYPE_IT)) + || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol)) + || ((m_dwSongFlags & SONG_ITCOMPATMODE) && (pChn->nRowInstr))) + { + if ((m_nType & MOD_TYPE_IT) && (pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol)) + { + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + pChn->dwFlags &= ~CHN_NOTEFADE; + pChn->nFadeOutVol = 65536; + } + if ((!bPorta) || (!(m_dwSongFlags & SONG_ITCOMPATMODE)) || (pChn->nRowInstr)) + { + if ((!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (pChn->nRowInstr)) + { + pChn->dwFlags &= ~CHN_NOTEFADE; + pChn->nFadeOutVol = 65536; + } + } + } + pChn->dwFlags &= ~(CHN_EXTRALOUD|CHN_KEYOFF); + // Enable Ramping + if (!bPorta) + { + pChn->nVUMeter = 0x100; + pChn->nLeftVU = pChn->nRightVU = 0xFF; + pChn->dwFlags &= ~CHN_FILTER; + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nRetrigCount = 0; + pChn->nTremorCount = 0; + if (bResetEnv) + { + pChn->nVolSwing = pChn->nPanSwing = 0; + if (penv) + { + if (!(penv->dwFlags & ENV_VOLCARRY)) pChn->nVolEnvPosition = 0; + if (!(penv->dwFlags & ENV_PANCARRY)) pChn->nPanEnvPosition = 0; + if (!(penv->dwFlags & ENV_PITCHCARRY)) pChn->nPitchEnvPosition = 0; + if (m_nType & MOD_TYPE_IT) + { + // Volume Swing + if (penv->nVolSwing) + { + int d = ((LONG)penv->nVolSwing*(LONG)((rand() & 0xFF) - 0x7F)) / 128; + pChn->nVolSwing = (signed short)((d * pChn->nVolume + 1)/128); + } + // Pan Swing + if (penv->nPanSwing) + { + int d = ((LONG)penv->nPanSwing*(LONG)((rand() & 0xFF) - 0x7F)) / 128; + pChn->nPanSwing = (signed short)d; + } + } + } + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } + pChn->nLeftVol = pChn->nRightVol = 0; + BOOL bFlt = (m_dwSongFlags & SONG_MPTFILTERMODE) ? FALSE : TRUE; + // Setup Initial Filter for this note + if (penv) + { + if (penv->nIFR & 0x80) { pChn->nResonance = penv->nIFR & 0x7F; bFlt = TRUE; } + if (penv->nIFC & 0x80) { pChn->nCutOff = penv->nIFC & 0x7F; bFlt = TRUE; } + } else + { + pChn->nVolSwing = pChn->nPanSwing = 0; + } +#ifndef NO_FILTER + if ((pChn->nCutOff < 0x7F) && (bFlt)) SetupChannelFilter(pChn, TRUE); +#endif // NO_FILTER + } +} + + +UINT CSoundFile::GetNNAChannel(UINT nChn) const +//--------------------------------------------- +{ + const MODCHANNEL *pChn = &Chn[nChn]; + // Check for empty channel + const MODCHANNEL *pi = &Chn[m_nChannels]; + for (UINT i=m_nChannels; i<MAX_CHANNELS; i++, pi++) if (!pi->nLength) return i; + if (!pChn->nFadeOutVol) return 0; + // All channels are used: check for lowest volume + UINT result = 0; + DWORD vol = 64*65536; // 25% + DWORD envpos = 0xFFFFFF; + const MODCHANNEL *pj = &Chn[m_nChannels]; + for (UINT j=m_nChannels; j<MAX_CHANNELS; j++, pj++) + { + if (!pj->nFadeOutVol) return j; + DWORD v = pj->nVolume; + if (pj->dwFlags & CHN_NOTEFADE) + v = v * pj->nFadeOutVol; + else + v <<= 16; + if (pj->dwFlags & CHN_LOOP) v >>= 1; + if ((v < vol) || ((v == vol) && (pj->nVolEnvPosition > envpos))) + { + envpos = pj->nVolEnvPosition; + vol = v; + result = j; + } + } + return result; +} + + +void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) +//------------------------------------------------------------------------ +{ + MODCHANNEL *pChn = &Chn[nChn]; + INSTRUMENTHEADER *penv = pChn->pHeader, *pHeader; + signed char *pSample; + if (note > 0x80) note = 0; + if (note < 1) return; + // Always NNA cut - using + if ((!(m_nType & (MOD_TYPE_IT|MOD_TYPE_MT2))) || (!m_nInstruments) || (bForceCut)) + { + if ((m_dwSongFlags & SONG_CPUVERYHIGH) + || (!pChn->nLength) || (pChn->dwFlags & CHN_MUTE) + || ((!pChn->nLeftVol) && (!pChn->nRightVol))) return; + UINT n = GetNNAChannel(nChn); + if (!n) return; + MODCHANNEL *p = &Chn[n]; + // Copy Channel + *p = *pChn; + p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO); + p->nMasterChn = nChn+1; + p->nCommand = 0; + // Cut the note + p->nFadeOutVol = 0; + p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + // Stop this channel + pChn->nLength = pChn->nPos = pChn->nPosLo = 0; + pChn->nROfs = pChn->nLOfs = 0; + pChn->nLeftVol = pChn->nRightVol = 0; + return; + } + if (instr >= MAX_INSTRUMENTS) instr = 0; + pSample = pChn->pSample; + pHeader = pChn->pHeader; + if ((instr) && (note)) + { + pHeader = Headers[instr]; + if (pHeader) + { + UINT n = 0; + if (note <= 0x80) + { + n = pHeader->Keyboard[note-1]; + note = pHeader->NoteMap[note-1]; + if ((n) && (n < MAX_SAMPLES)) pSample = Ins[n].pSample; + } + } else pSample = NULL; + } + if (!penv) return; + MODCHANNEL *p = pChn; + for (UINT i=nChn; i<MAX_CHANNELS; p++, i++) + if ((i >= m_nChannels) || (p == pChn)) + { + if (((p->nMasterChn == nChn+1) || (p == pChn)) && (p->pHeader)) + { + BOOL bOk = FALSE; + // Duplicate Check Type + switch(p->pHeader->nDCT) + { + // Note + case DCT_NOTE: + if ((note) && (p->nNote == note) && (pHeader == p->pHeader)) bOk = TRUE; + break; + // Sample + case DCT_SAMPLE: + if ((pSample) && (pSample == p->pSample)) bOk = TRUE; + break; + // Instrument + case DCT_INSTRUMENT: + if (pHeader == p->pHeader) bOk = TRUE; + break; + } + // Duplicate Note Action + if (bOk) + { + switch(p->pHeader->nDNA) + { + // Cut + case DNA_NOTECUT: + KeyOff(i); + p->nVolume = 0; + break; + // Note Off + case DNA_NOTEOFF: + KeyOff(i); + break; + // Note Fade + case DNA_NOTEFADE: + p->dwFlags |= CHN_NOTEFADE; + break; + } + if (!p->nVolume) + { + p->nFadeOutVol = 0; + p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + } + } + } + } + if (pChn->dwFlags & CHN_MUTE) return; + // New Note Action + if ((pChn->nVolume) && (pChn->nLength)) + { + UINT n = GetNNAChannel(nChn); + if (n) + { + MODCHANNEL *p = &Chn[n]; + // Copy Channel + *p = *pChn; + p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO); + p->nMasterChn = nChn+1; + p->nCommand = 0; + // Key Off the note + switch(pChn->nNNA) + { + case NNA_NOTEOFF: KeyOff(n); break; + case NNA_NOTECUT: + p->nFadeOutVol = 0; + case NNA_NOTEFADE: p->dwFlags |= CHN_NOTEFADE; break; + } + if (!p->nVolume) + { + p->nFadeOutVol = 0; + p->dwFlags |= (CHN_NOTEFADE|CHN_FASTVOLRAMP); + } + // Stop this channel + pChn->nLength = pChn->nPos = pChn->nPosLo = 0; + pChn->nROfs = pChn->nLOfs = 0; + } + } +} + + +BOOL CSoundFile::ProcessEffects() +//------------------------------- +{ + int nBreakRow = -1, nPosJump = -1, nPatLoopRow = -1; + MODCHANNEL *pChn = Chn; + for (UINT nChn=0; nChn<m_nChannels; nChn++, pChn++) + { + UINT instr = pChn->nRowInstr; + UINT volcmd = pChn->nRowVolCmd; + UINT vol = pChn->nRowVolume; + UINT cmd = pChn->nRowCommand; + UINT param = pChn->nRowParam; + BOOL bPorta = ((cmd != CMD_TONEPORTAMENTO) && (cmd != CMD_TONEPORTAVOL) && (volcmd != VOLCMD_TONEPORTAMENTO)) ? FALSE : TRUE; + UINT nStartTick = 0; + + pChn->dwFlags &= ~CHN_FASTVOLRAMP; + // Process special effects (note delay, pattern delay, pattern loop) + if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX)) + { + if ((!param) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) param = pChn->nOldCmdEx; else pChn->nOldCmdEx = param; + // Note Delay ? + if ((param & 0xF0) == 0xD0) + { + nStartTick = param & 0x0F; + } else + if (!m_nTickCount) + { + // Pattern Loop ? + if ((((param & 0xF0) == 0x60) && (cmd == CMD_MODCMDEX)) + || (((param & 0xF0) == 0xB0) && (cmd == CMD_S3MCMDEX))) + { + int nloop = PatternLoop(pChn, param & 0x0F); + if (nloop >= 0) nPatLoopRow = nloop; + } else + // Pattern Delay + if ((param & 0xF0) == 0xE0) + { + m_nPatternDelay = param & 0x0F; + } + } + } + + // Handles note/instrument/volume changes + if (m_nTickCount == nStartTick) // can be delayed by a note delay effect + { + UINT note = pChn->nRowNote; + if (instr) pChn->nNewIns = instr; + // XM: Key-Off + Sample == Note Cut + if (m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if ((note == 0xFF) && ((!pChn->pHeader) || (!(pChn->pHeader->dwFlags & ENV_VOLUME)))) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nVolume = 0; + note = instr = 0; + } + } + if ((!note) && (instr)) + { + if (m_nInstruments) + { + if (pChn->pInstrument) pChn->nVolume = pChn->pInstrument->nVolume; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + pChn->dwFlags &= ~CHN_NOTEFADE; + pChn->nFadeOutVol = 65536; + } + } else + { + if (instr < MAX_SAMPLES) pChn->nVolume = Ins[instr].nVolume; + } + if (!(m_nType & MOD_TYPE_IT)) instr = 0; + } + // Invalid Instrument ? + if (instr >= MAX_INSTRUMENTS) instr = 0; + // Note Cut/Off => ignore instrument + if (note >= 0xFE) instr = 0; + if ((note) && (note <= 128)) pChn->nNewNote = note; + // New Note Action ? + if ((note) && (note <= 128) && (!bPorta)) + { + CheckNNA(nChn, instr, note, FALSE); + } + // Instrument Change ? + if (instr) + { + MODINSTRUMENT *psmp = pChn->pInstrument; + InstrumentChange(pChn, instr, bPorta, TRUE); + pChn->nNewIns = 0; + // Special IT case: portamento+note causes sample change -> ignore portamento + if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) + && (psmp != pChn->pInstrument) && (note) && (note < 0x80)) + { + bPorta = FALSE; + } + } + // New Note ? + if (note) + { + if ((!instr) && (pChn->nNewIns) && (note < 0x80)) + { + InstrumentChange(pChn, pChn->nNewIns, bPorta, FALSE, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); + pChn->nNewIns = 0; + } + NoteChange(nChn, note, bPorta, (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? FALSE : TRUE); + if ((bPorta) && (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) && (instr)) + { + pChn->dwFlags |= CHN_FASTVOLRAMP; + pChn->nVolEnvPosition = 0; + pChn->nPanEnvPosition = 0; + pChn->nPitchEnvPosition = 0; + pChn->nAutoVibDepth = 0; + pChn->nAutoVibPos = 0; + } + } + // Tick-0 only volume commands + if (volcmd == VOLCMD_VOLUME) + { + if (vol > 64) vol = 64; + pChn->nVolume = vol << 2; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } else + if (volcmd == VOLCMD_PANNING) + { + if (vol > 64) vol = 64; + pChn->nPan = vol << 2; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + } + + // Volume Column Effect (except volume & panning) + if ((volcmd > VOLCMD_PANNING) && (m_nTickCount >= nStartTick)) + { + if (volcmd == VOLCMD_TONEPORTAMENTO) + { + if (m_nType & MOD_TYPE_IT) + TonePortamento(pChn, ImpulseTrackerPortaVolCmd[vol & 0x0F]); + else + TonePortamento(pChn, vol * 16); + } else + { + if (vol) pChn->nOldVolParam = vol; else vol = pChn->nOldVolParam; + switch(volcmd) + { + case VOLCMD_VOLSLIDEUP: + VolumeSlide(pChn, vol << 4); + break; + + case VOLCMD_VOLSLIDEDOWN: + VolumeSlide(pChn, vol); + break; + + case VOLCMD_FINEVOLUP: + if (m_nType & MOD_TYPE_IT) + { + if (m_nTickCount == nStartTick) VolumeSlide(pChn, (vol << 4) | 0x0F); + } else + FineVolumeUp(pChn, vol); + break; + + case VOLCMD_FINEVOLDOWN: + if (m_nType & MOD_TYPE_IT) + { + if (m_nTickCount == nStartTick) VolumeSlide(pChn, 0xF0 | vol); + } else + FineVolumeDown(pChn, vol); + break; + + case VOLCMD_VIBRATOSPEED: + Vibrato(pChn, vol << 4); + break; + + case VOLCMD_VIBRATO: + Vibrato(pChn, vol); + break; + + case VOLCMD_PANSLIDELEFT: + PanningSlide(pChn, vol); + break; + + case VOLCMD_PANSLIDERIGHT: + PanningSlide(pChn, vol << 4); + break; + + case VOLCMD_PORTAUP: + PortamentoUp(pChn, vol << 2); + break; + + case VOLCMD_PORTADOWN: + PortamentoDown(pChn, vol << 2); + break; + } + } + } + + // Effects + if (cmd) switch (cmd) + { + // Set Volume + case CMD_VOLUME: + if (!m_nTickCount) + { + pChn->nVolume = (param < 64) ? param*4 : 256; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + break; + + // Portamento Up + case CMD_PORTAMENTOUP: + if ((!param) && (m_nType & MOD_TYPE_MOD)) break; + PortamentoUp(pChn, param); + break; + + // Portamento Down + case CMD_PORTAMENTODOWN: + if ((!param) && (m_nType & MOD_TYPE_MOD)) break; + PortamentoDown(pChn, param); + break; + + // Volume Slide + case CMD_VOLUMESLIDE: + if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); + break; + + // Tone-Portamento + case CMD_TONEPORTAMENTO: + TonePortamento(pChn, param); + break; + + // Tone-Portamento + Volume Slide + case CMD_TONEPORTAVOL: + if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); + TonePortamento(pChn, 0); + break; + + // Vibrato + case CMD_VIBRATO: + Vibrato(pChn, param); + break; + + // Vibrato + Volume Slide + case CMD_VIBRATOVOL: + if ((param) || (m_nType != MOD_TYPE_MOD)) VolumeSlide(pChn, param); + Vibrato(pChn, 0); + break; + + // Set Speed + case CMD_SPEED: + if (!m_nTickCount) SetSpeed(param); + break; + + // Set Tempo + case CMD_TEMPO: + if (!m_nTickCount) + { + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) + { + if (param) pChn->nOldTempo = param; else param = pChn->nOldTempo; + } + SetTempo(param); + } + break; + + // Set Offset + case CMD_OFFSET: + if (m_nTickCount) break; + if (param) pChn->nOldOffset = param; else param = pChn->nOldOffset; + param <<= 8; + param |= (UINT)(pChn->nOldHiOffset) << 16; + if ((pChn->nRowNote) && (pChn->nRowNote < 0x80)) + { + if (bPorta) + pChn->nPos = param; + else + pChn->nPos += param; + if (pChn->nPos >= pChn->nLength) + { + if (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) + { + pChn->nPos = pChn->nLoopStart; + if ((m_dwSongFlags & SONG_ITOLDEFFECTS) && (pChn->nLength > 4)) + { + pChn->nPos = pChn->nLength - 2; + } + } + } + } else + if ((param < pChn->nLength) && (m_nType & (MOD_TYPE_MTM|MOD_TYPE_DMF))) + { + pChn->nPos = param; + } + break; + + // Arpeggio + case CMD_ARPEGGIO: + if ((m_nTickCount) || (!pChn->nPeriod) || (!pChn->nNote)) break; + if ((!param) && (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)))) break; + pChn->nCommand = CMD_ARPEGGIO; + if (param) pChn->nArpeggio = param; + break; + + // Retrig + case CMD_RETRIG: + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (!(param & 0xF0)) param |= pChn->nRetrigParam & 0xF0; + if (!(param & 0x0F)) param |= pChn->nRetrigParam & 0x0F; + param |= 0x100; // increment retrig count on first row + } + if (param) pChn->nRetrigParam = (BYTE)(param & 0xFF); else param = pChn->nRetrigParam; + RetrigNote(nChn, param); + break; + + // Tremor + case CMD_TREMOR: + if (m_nTickCount) break; + pChn->nCommand = CMD_TREMOR; + if (param) pChn->nTremorParam = param; + break; + + // Set Global Volume + case CMD_GLOBALVOLUME: + if (m_nTickCount) break; + if (m_nType != MOD_TYPE_IT) param <<= 1; + if (param > 128) param = 128; + m_nGlobalVolume = param << 1; + break; + + // Global Volume Slide + case CMD_GLOBALVOLSLIDE: + GlobalVolSlide(param); + break; + + // Set 8-bit Panning + case CMD_PANNING8: + if (m_nTickCount) break; + if (!(m_dwSongFlags & SONG_SURROUNDPAN)) pChn->dwFlags &= ~CHN_SURROUND; + if (m_nType & (MOD_TYPE_IT|MOD_TYPE_XM|MOD_TYPE_MT2)) + { + pChn->nPan = param; + } else + if (param <= 0x80) + { + pChn->nPan = param << 1; + } else + if (param == 0xA4) + { + pChn->dwFlags |= CHN_SURROUND; + pChn->nPan = 0x80; + } + pChn->dwFlags |= CHN_FASTVOLRAMP; + break; + + // Panning Slide + case CMD_PANNINGSLIDE: + PanningSlide(pChn, param); + break; + + // Tremolo + case CMD_TREMOLO: + Tremolo(pChn, param); + break; + + // Fine Vibrato + case CMD_FINEVIBRATO: + FineVibrato(pChn, param); + break; + + // MOD/XM Exx Extended Commands + case CMD_MODCMDEX: + ExtendedMODCommands(nChn, param); + break; + + // S3M/IT Sxx Extended Commands + case CMD_S3MCMDEX: + ExtendedS3MCommands(nChn, param); + break; + + // Key Off + case CMD_KEYOFF: + if (!m_nTickCount) KeyOff(nChn); + break; + + // Extra-fine porta up/down + case CMD_XFINEPORTAUPDOWN: + switch(param & 0xF0) + { + case 0x10: ExtraFinePortamentoUp(pChn, param & 0x0F); break; + case 0x20: ExtraFinePortamentoDown(pChn, param & 0x0F); break; + // Modplug XM Extensions + case 0x50: + case 0x60: + case 0x70: + case 0x90: + case 0xA0: ExtendedS3MCommands(nChn, param); break; + } + break; + + // Set Channel Global Volume + case CMD_CHANNELVOLUME: + if (m_nTickCount) break; + if (param <= 64) + { + pChn->nGlobalVol = param; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + break; + + // Channel volume slide + case CMD_CHANNELVOLSLIDE: + ChannelVolSlide(pChn, param); + break; + + // Panbrello (IT) + case CMD_PANBRELLO: + Panbrello(pChn, param); + break; + + // Set Envelope Position + case CMD_SETENVPOSITION: + if (!m_nTickCount) + { + pChn->nVolEnvPosition = param; + pChn->nPanEnvPosition = param; + pChn->nPitchEnvPosition = param; + if (pChn->pHeader) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + if ((pChn->dwFlags & CHN_PANENV) && (penv->nPanEnv) && (param > penv->PanPoints[penv->nPanEnv-1])) + { + pChn->dwFlags &= ~CHN_PANENV; + } + } + } + break; + + // Position Jump + case CMD_POSITIONJUMP: + nPosJump = param; + break; + + // Pattern Break + case CMD_PATTERNBREAK: + nBreakRow = param; + break; + + // Midi Controller + case CMD_MIDI: + if (m_nTickCount) break; + if (param < 0x80) + { + ProcessMidiMacro(nChn, &m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro << 5], param); + } else + { + ProcessMidiMacro(nChn, &m_MidiCfg.szMidiZXXExt[(param & 0x7F) << 5], 0); + } + break; + } + } + + // Navigation Effects + if (!m_nTickCount) + { + // Pattern Loop + if (nPatLoopRow >= 0) + { + m_nNextPattern = m_nCurrentPattern; + m_nNextRow = nPatLoopRow; + if (m_nPatternDelay) m_nNextRow++; + } else + // Pattern Break / Position Jump only if no loop running + if ((nBreakRow >= 0) || (nPosJump >= 0)) + { + BOOL bNoLoop = FALSE; + if (nPosJump < 0) nPosJump = m_nCurrentPattern+1; + if (nBreakRow < 0) nBreakRow = 0; + // Modplug Tracker & ModPlugin allow backward jumps + #ifndef FASTSOUNDLIB + if ((nPosJump < (int)m_nCurrentPattern) + || ((nPosJump == (int)m_nCurrentPattern) && (nBreakRow <= (int)m_nRow))) + { + if (!IsValidBackwardJump(m_nCurrentPattern, m_nRow, nPosJump, nBreakRow)) + { + if (m_nRepeatCount) + { + if (m_nRepeatCount > 0) m_nRepeatCount--; + } else + { + #ifdef MODPLUG_TRACKER + if (gdwSoundSetup & SNDMIX_NOBACKWARDJUMPS) + #endif + // Backward jump disabled + bNoLoop = TRUE; + //reset repeat count incase there are multiple loops. + //(i.e. Unreal tracks) + m_nRepeatCount = m_nInitialRepeatCount; + } + } + } + #endif // FASTSOUNDLIB + if (((!bNoLoop) && (nPosJump < MAX_ORDERS)) + && ((nPosJump != (int)m_nCurrentPattern) || (nBreakRow != (int)m_nRow))) + { + if (nPosJump != (int)m_nCurrentPattern) + { + for (UINT i=0; i<m_nChannels; i++) Chn[i].nPatternLoopCount = 0; + } + m_nNextPattern = nPosJump; + m_nNextRow = (UINT)nBreakRow; + } + } + } + return TRUE; +} + + +//////////////////////////////////////////////////////////// +// Channels effects + +void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------- +{ + if (param) pChn->nOldPortaUpDown = param; else param = pChn->nOldPortaUpDown; + if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0)) + { + if (param & 0x0F) + { + if ((param & 0xF0) == 0xF0) + { + FinePortamentoUp(pChn, param & 0x0F); + } else + if ((param & 0xF0) == 0xE0) + { + ExtraFinePortamentoUp(pChn, param & 0x0F); + } + } + return; + } + // Regular Slide + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + DoFreqSlide(pChn, -(int)(param * 4)); + } +} + + +void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) +//----------------------------------------------------------- +{ + if (param) pChn->nOldPortaUpDown = param; else param = pChn->nOldPortaUpDown; + if ((m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0)) + { + if (param & 0x0F) + { + if ((param & 0xF0) == 0xF0) + { + FinePortamentoDown(pChn, param & 0x0F); + } else + if ((param & 0xF0) == 0xE0) + { + ExtraFinePortamentoDown(pChn, param & 0x0F); + } + } + return; + } + if (!(m_dwSongFlags & SONG_FIRSTTICK)) DoFreqSlide(pChn, (int)(param << 2)); +} + + +void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------- +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideDownTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod -= (int)(param * 4); + } + if (pChn->nPeriod < 1) pChn->nPeriod = 1; + } + } +} + + +void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------------- +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideUpTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod += (int)(param * 4); + } + if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; + } + } +} + + +void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------------ +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, FineLinearSlideDownTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod -= (int)(param); + } + if (pChn->nPeriod < 1) pChn->nPeriod = 1; + } + } +} + + +void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) +//-------------------------------------------------------------------- +{ + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown; + } + if (m_dwSongFlags & SONG_FIRSTTICK) + { + if ((pChn->nPeriod) && (param)) + { + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + pChn->nPeriod = _muldivr(pChn->nPeriod, FineLinearSlideUpTable[param & 0x0F], 65536); + } else + { + pChn->nPeriod += (int)(param); + } + if (pChn->nPeriod > 0xFFFF) pChn->nPeriod = 0xFFFF; + } + } +} + + +// Portamento Slide +void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) +//----------------------------------------------------------- +{ + if (param) pChn->nPortamentoSlide = param * 4; + pChn->dwFlags |= CHN_PORTAMENTO; + if ((pChn->nPeriod) && (pChn->nPortamentoDest) && (!(m_dwSongFlags & SONG_FIRSTTICK))) + { + if (pChn->nPeriod < pChn->nPortamentoDest) + { + LONG delta = (int)pChn->nPortamentoSlide; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + UINT n = pChn->nPortamentoSlide >> 2; + if (n > 255) n = 255; + delta = _muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536) - pChn->nPeriod; + if (delta < 1) delta = 1; + } + pChn->nPeriod += delta; + if (pChn->nPeriod > pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; + } else + if (pChn->nPeriod > pChn->nPortamentoDest) + { + LONG delta = - (int)pChn->nPortamentoSlide; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + UINT n = pChn->nPortamentoSlide >> 2; + if (n > 255) n = 255; + delta = _muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536) - pChn->nPeriod; + if (delta > -1) delta = -1; + } + pChn->nPeriod += delta; + if (pChn->nPeriod < pChn->nPortamentoDest) pChn->nPeriod = pChn->nPortamentoDest; + } + } +} + + +void CSoundFile::Vibrato(MODCHANNEL *p, UINT param) +//------------------------------------------------- +{ + if (param & 0x0F) p->nVibratoDepth = (param & 0x0F) * 4; + if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_VIBRATO; +} + + +void CSoundFile::FineVibrato(MODCHANNEL *p, UINT param) +//----------------------------------------------------- +{ + if (param & 0x0F) p->nVibratoDepth = param & 0x0F; + if (param & 0xF0) p->nVibratoSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_VIBRATO; +} + + +void CSoundFile::Panbrello(MODCHANNEL *p, UINT param) +//--------------------------------------------------- +{ + if (param & 0x0F) p->nPanbrelloDepth = param & 0x0F; + if (param & 0xF0) p->nPanbrelloSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_PANBRELLO; +} + + +void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) +//-------------------------------------------------------- +{ + if (param) pChn->nOldVolumeSlide = param; else param = pChn->nOldVolumeSlide; + LONG newvolume = pChn->nVolume; + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_AMF)) + { + if ((param & 0x0F) == 0x0F) + { + if (param & 0xF0) + { + FineVolumeUp(pChn, (param >> 4)); + return; + } else + { + if ((m_dwSongFlags & SONG_FIRSTTICK) && (!(m_dwSongFlags & SONG_FASTVOLSLIDES))) + { + newvolume -= 0x0F * 4; + } + } + } else + if ((param & 0xF0) == 0xF0) + { + if (param & 0x0F) + { + FineVolumeDown(pChn, (param & 0x0F)); + return; + } else + { + if ((m_dwSongFlags & SONG_FIRSTTICK) && (!(m_dwSongFlags & SONG_FASTVOLSLIDES))) + { + newvolume += 0x0F * 4; + } + } + } + } + if ((!(m_dwSongFlags & SONG_FIRSTTICK)) || (m_dwSongFlags & SONG_FASTVOLSLIDES)) + { + if (param & 0x0F) newvolume -= (int)((param & 0x0F) * 4); + else newvolume += (int)((param & 0xF0) >> 2); + if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; + } + if (newvolume < 0) newvolume = 0; + if (newvolume > 256) newvolume = 256; + pChn->nVolume = newvolume; +} + + +void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------- +{ + LONG nPanSlide = 0; + if (param) pChn->nOldPanSlide = param; else param = pChn->nOldPanSlide; + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM)) + { + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) + { + param = (param & 0xF0) >> 2; + nPanSlide = - (int)param; + } + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) + { + nPanSlide = (param & 0x0F) << 2; + } + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0x0F) nPanSlide = (int)((param & 0x0F) << 2); + else nPanSlide = -(int)((param & 0xF0) >> 2); + } + } + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0x0F) nPanSlide = -(int)((param & 0x0F) << 2); + else nPanSlide = (int)((param & 0xF0) >> 2); + } + } + if (nPanSlide) + { + nPanSlide += pChn->nPan; + if (nPanSlide < 0) nPanSlide = 0; + if (nPanSlide > 256) nPanSlide = 256; + pChn->nPan = nPanSlide; + } +} + + +void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) +//--------------------------------------------------------- +{ + if (param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; + if (m_dwSongFlags & SONG_FIRSTTICK) + { + pChn->nVolume += param * 4; + if (pChn->nVolume > 256) pChn->nVolume = 256; + if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; + } +} + + +void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) +//----------------------------------------------------------- +{ + if (param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown; + if (m_dwSongFlags & SONG_FIRSTTICK) + { + pChn->nVolume -= param * 4; + if (pChn->nVolume < 0) pChn->nVolume = 0; + if (m_nType & MOD_TYPE_MOD) pChn->dwFlags |= CHN_FASTVOLRAMP; + } +} + + +void CSoundFile::Tremolo(MODCHANNEL *p, UINT param) +//------------------------------------------------- +{ + if (param & 0x0F) p->nTremoloDepth = (param & 0x0F) << 2; + if (param & 0xF0) p->nTremoloSpeed = (param >> 4) & 0x0F; + p->dwFlags |= CHN_TREMOLO; +} + + +void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------ +{ + LONG nChnSlide = 0; + if (param) pChn->nOldChnVolSlide = param; else param = pChn->nOldChnVolSlide; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nChnSlide = param >> 4; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nChnSlide = - (int)(param & 0x0F); + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0x0F) nChnSlide = -(int)(param & 0x0F); + else nChnSlide = (int)((param & 0xF0) >> 4); + } + } + if (nChnSlide) + { + nChnSlide += pChn->nGlobalVol; + if (nChnSlide < 0) nChnSlide = 0; + if (nChnSlide > 64) nChnSlide = 64; + pChn->nGlobalVol = nChnSlide; + } +} + + +void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) +//--------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + UINT command = param & 0xF0; + param &= 0x0F; + switch(command) + { + // E0x: Set Filter + // E1x: Fine Portamento Up + case 0x10: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoUp(pChn, param); break; + // E2x: Fine Portamento Down + case 0x20: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FinePortamentoDown(pChn, param); break; + // E3x: Set Glissando Control + case 0x30: pChn->dwFlags &= ~CHN_GLISSANDO; if (param) pChn->dwFlags |= CHN_GLISSANDO; break; + // E4x: Set Vibrato WaveForm + case 0x40: pChn->nVibratoType = param & 0x07; break; + // E5x: Set FineTune + case 0x50: if (m_nTickCount) break; + pChn->nC4Speed = S3MFineTuneTable[param]; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + pChn->nFineTune = param*2; + else + pChn->nFineTune = MOD2XMFineTune(param); + if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC4Speed); + break; + // E6x: Pattern Loop + // E7x: Set Tremolo WaveForm + case 0x70: pChn->nTremoloType = param & 0x07; break; + // E8x: Set 4-bit Panning + case 0x80: if (!m_nTickCount) { pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; + // E9x: Retrig + case 0x90: RetrigNote(nChn, param); break; + // EAx: Fine Volume Up + case 0xA0: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeUp(pChn, param); break; + // EBx: Fine Volume Down + case 0xB0: if ((param) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) FineVolumeDown(pChn, param); break; + // ECx: Note Cut + case 0xC0: NoteCut(nChn, param); break; + // EDx: Note Delay + // EEx: Pattern Delay + // EFx: MOD: Invert Loop, XM: Set Active Midi Macro + case 0xF0: pChn->nActiveMacro = param; break; + } +} + + +void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) +//--------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + UINT command = param & 0xF0; + param &= 0x0F; + switch(command) + { + // S0x: Set Filter + // S1x: Set Glissando Control + case 0x10: pChn->dwFlags &= ~CHN_GLISSANDO; if (param) pChn->dwFlags |= CHN_GLISSANDO; break; + // S2x: Set FineTune + case 0x20: if (m_nTickCount) break; + pChn->nC4Speed = S3MFineTuneTable[param & 0x0F]; + pChn->nFineTune = MOD2XMFineTune(param); + if (pChn->nPeriod) pChn->nPeriod = GetPeriodFromNote(pChn->nNote, pChn->nFineTune, pChn->nC4Speed); + break; + // S3x: Set Vibrato WaveForm + case 0x30: pChn->nVibratoType = param & 0x07; break; + // S4x: Set Tremolo WaveForm + case 0x40: pChn->nTremoloType = param & 0x07; break; + // S5x: Set Panbrello WaveForm + case 0x50: pChn->nPanbrelloType = param & 0x07; break; + // S6x: Pattern Delay for x frames + case 0x60: m_nFrameDelay = param; break; + // S7x: Envelope Control + case 0x70: if (m_nTickCount) break; + switch(param) + { + case 0: + case 1: + case 2: + { + MODCHANNEL *bkp = &Chn[m_nChannels]; + for (UINT i=m_nChannels; i<MAX_CHANNELS; i++, bkp++) + { + if (bkp->nMasterChn == nChn+1) + { + if (param == 1) KeyOff(i); else + if (param == 2) bkp->dwFlags |= CHN_NOTEFADE; else + { bkp->dwFlags |= CHN_NOTEFADE; bkp->nFadeOutVol = 0; } + } + } + } + break; + case 3: pChn->nNNA = NNA_NOTECUT; break; + case 4: pChn->nNNA = NNA_CONTINUE; break; + case 5: pChn->nNNA = NNA_NOTEOFF; break; + case 6: pChn->nNNA = NNA_NOTEFADE; break; + case 7: pChn->dwFlags &= ~CHN_VOLENV; break; + case 8: pChn->dwFlags |= CHN_VOLENV; break; + case 9: pChn->dwFlags &= ~CHN_PANENV; break; + case 10: pChn->dwFlags |= CHN_PANENV; break; + case 11: pChn->dwFlags &= ~CHN_PITCHENV; break; + case 12: pChn->dwFlags |= CHN_PITCHENV; break; + } + break; + // S8x: Set 4-bit Panning + case 0x80: if (!m_nTickCount) { pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP; } break; + // S9x: Set Surround + case 0x90: ExtendedChannelEffect(pChn, param & 0x0F); break; + // SAx: Set 64k Offset + case 0xA0: if (!m_nTickCount) + { + pChn->nOldHiOffset = param; + if ((pChn->nRowNote) && (pChn->nRowNote < 0x80)) + { + DWORD pos = param << 16; + if (pos < pChn->nLength) pChn->nPos = pos; + } + } + break; + // SBx: Pattern Loop + // SCx: Note Cut + case 0xC0: NoteCut(nChn, param); break; + // SDx: Note Delay + // case 0xD0: break; + // SEx: Pattern Delay for x rows + // SFx: S3M: Funk Repeat, IT: Set Active Midi Macro + case 0xF0: pChn->nActiveMacro = param; break; + } +} + + +void CSoundFile::ExtendedChannelEffect(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------------------ +{ + // S9x and X9x commands (S3M/XM/IT only) + if (m_nTickCount) return; + switch(param & 0x0F) + { + // S90: Surround Off + case 0x00: pChn->dwFlags &= ~CHN_SURROUND; break; + // S91: Surround On + case 0x01: pChn->dwFlags |= CHN_SURROUND; pChn->nPan = 128; break; + //////////////////////////////////////////////////////////// + // Modplug Extensions + // S98: Reverb Off + case 0x08: + pChn->dwFlags &= ~CHN_REVERB; + pChn->dwFlags |= CHN_NOREVERB; + break; + // S99: Reverb On + case 0x09: + pChn->dwFlags &= ~CHN_NOREVERB; + pChn->dwFlags |= CHN_REVERB; + break; + // S9A: 2-Channels surround mode + case 0x0A: + m_dwSongFlags &= ~SONG_SURROUNDPAN; + break; + // S9B: 4-Channels surround mode + case 0x0B: + m_dwSongFlags |= SONG_SURROUNDPAN; + break; + // S9C: IT Filter Mode + case 0x0C: + m_dwSongFlags &= ~SONG_MPTFILTERMODE; + break; + // S9D: MPT Filter Mode + case 0x0D: + m_dwSongFlags |= SONG_MPTFILTERMODE; + break; + // S9E: Go forward + case 0x0E: + pChn->dwFlags &= ~(CHN_PINGPONGFLAG); + break; + // S9F: Go backward (set position at the end for non-looping samples) + case 0x0F: + if ((!(pChn->dwFlags & CHN_LOOP)) && (!pChn->nPos) && (pChn->nLength)) + { + pChn->nPos = pChn->nLength - 1; + pChn->nPosLo = 0xFFFF; + } + pChn->dwFlags |= CHN_PINGPONGFLAG; + break; + } +} + + +void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) +//--------------------------------------------------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + DWORD dwMacro = (*((LPDWORD)pszMidiMacro)) & 0x7F5F7F5F; + // Not Internal Device ? + if (dwMacro != 0x30463046) + { + UINT pos = 0, nNib = 0, nBytes = 0; + DWORD dwMidiCode = 0, dwByteCode = 0; + while (pos+6 <= 32) + { + CHAR cData = pszMidiMacro[pos++]; + if (!cData) break; + if ((cData >= '0') && (cData <= '9')) { dwByteCode = (dwByteCode<<4) | (cData-'0'); nNib++; } else + if ((cData >= 'A') && (cData <= 'F')) { dwByteCode = (dwByteCode<<4) | (cData-'A'+10); nNib++; } else + if ((cData >= 'a') && (cData <= 'f')) { dwByteCode = (dwByteCode<<4) | (cData-'a'+10); nNib++; } else + if ((cData == 'z') || (cData == 'Z')) { dwByteCode = param & 0x7f; nNib = 2; } else + if ((cData == 'x') || (cData == 'X')) { dwByteCode = param & 0x70; nNib = 2; } else + if ((cData == 'y') || (cData == 'Y')) { dwByteCode = (param & 0x0f)<<3; nNib = 2; } else + if (nNib >= 2) + { + nNib = 0; + dwMidiCode |= dwByteCode << (nBytes*8); + dwByteCode = 0; + nBytes++; + if (nBytes >= 3) + { + UINT nMasterCh = (nChn < m_nChannels) ? nChn+1 : pChn->nMasterChn; + if ((nMasterCh) && (nMasterCh <= m_nChannels)) + { + UINT nPlug = ChnSettings[nMasterCh-1].nMixPlugin; + if ((nPlug) && (nPlug <= MAX_MIXPLUGINS)) + { + IMixPlugin *pPlugin = m_MixPlugins[nPlug-1].pMixPlugin; + if ((pPlugin) && (m_MixPlugins[nPlug-1].pMixState)) + { + pPlugin->MidiSend(dwMidiCode); + } + } + } + nBytes = 0; + dwMidiCode = 0; + } + } + + } + return; + } + // Internal device + pszMidiMacro += 4; + // Filter ? + if (pszMidiMacro[0] == '0') + { + CHAR cData1 = pszMidiMacro[2]; + DWORD dwParam = 0; + if ((cData1 == 'z') || (cData1 == 'Z')) + { + dwParam = param; + } else + { + CHAR cData2 = pszMidiMacro[3]; + if ((cData1 >= '0') && (cData1 <= '9')) dwParam += (cData1 - '0') << 4; else + if ((cData1 >= 'A') && (cData1 <= 'F')) dwParam += (cData1 - 'A' + 0x0A) << 4; + if ((cData2 >= '0') && (cData2 <= '9')) dwParam += (cData2 - '0'); else + if ((cData2 >= 'A') && (cData2 <= 'F')) dwParam += (cData2 - 'A' + 0x0A); + } + switch(pszMidiMacro[1]) + { + // F0.F0.00.xx: Set CutOff + case '0': + { + int oldcutoff = pChn->nCutOff; + if (dwParam < 0x80) pChn->nCutOff = dwParam; +#ifndef NO_FILTER + oldcutoff -= pChn->nCutOff; + + if (oldcutoff < 0) oldcutoff = -oldcutoff; + if ((pChn->nVolume > 0) || (oldcutoff < 0x10) + || (!(pChn->dwFlags & CHN_FILTER)) || (!(pChn->nLeftVol|pChn->nRightVol))) + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE); +#endif // NO_FILTER + } + break; + + // F0.F0.01.xx: Set Resonance + case '1': + if (dwParam < 0x80) pChn->nResonance = dwParam; +#ifndef NO_FILTER + SetupChannelFilter(pChn, (pChn->dwFlags & CHN_FILTER) ? FALSE : TRUE); +#endif // NO_FILTER + + break; + } + + } +} + + +void CSoundFile::RetrigNote(UINT nChn, UINT param) +//------------------------------------------------ +{ + // Retrig: bit 8 is set if it's the new XM retrig + MODCHANNEL *pChn = &Chn[nChn]; + UINT nRetrigSpeed = param & 0x0F; + UINT nRetrigCount = pChn->nRetrigCount; + BOOL bDoRetrig = FALSE; + + if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT)) + { + if (!nRetrigSpeed) nRetrigSpeed = 1; + if ((nRetrigCount) && (!(nRetrigCount % nRetrigSpeed))) bDoRetrig = TRUE; + nRetrigCount++; + } else + { + UINT realspeed = nRetrigSpeed; + if ((param & 0x100) && (pChn->nRowVolCmd == VOLCMD_VOLUME) && (pChn->nRowParam & 0xF0)) realspeed++; + if ((m_nTickCount) || (param & 0x100)) + { + if (!realspeed) realspeed = 1; + if ((!(param & 0x100)) && (m_nMusicSpeed) && (!(m_nTickCount % realspeed))) bDoRetrig = TRUE; + nRetrigCount++; + } else if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) nRetrigCount = 0; + if (nRetrigCount >= realspeed) + { + if ((m_nTickCount) || ((param & 0x100) && (!pChn->nRowNote))) bDoRetrig = TRUE; + } + } + if (bDoRetrig) + { + UINT dv = (param >> 4) & 0x0F; + if (dv) + { + int vol = pChn->nVolume; + if (retrigTable1[dv]) + vol = (vol * retrigTable1[dv]) >> 4; + else + vol += ((int)retrigTable2[dv]) << 2; + if (vol < 0) vol = 0; + if (vol > 256) vol = 256; + pChn->nVolume = vol; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } + UINT nNote = pChn->nNewNote; + LONG nOldPeriod = pChn->nPeriod; + if ((nNote) && (nNote <= 120) && (pChn->nLength)) CheckNNA(nChn, 0, nNote, TRUE); + BOOL bResetEnv = FALSE; + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if ((pChn->nRowInstr) && (param < 0x100)) { InstrumentChange(pChn, pChn->nRowInstr, FALSE, FALSE); bResetEnv = TRUE; } + if (param < 0x100) bResetEnv = TRUE; + } + NoteChange(nChn, nNote, FALSE, bResetEnv); + if ((m_nType & MOD_TYPE_IT) && (!pChn->nRowNote) && (nOldPeriod)) pChn->nPeriod = nOldPeriod; + if (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT))) nRetrigCount = 0; + } + pChn->nRetrigCount = (BYTE)nRetrigCount; +} + + +void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) +//------------------------------------------------------------- +{ + // IT Linear slides + if (!pChn->nPeriod) return; + if ((m_dwSongFlags & SONG_LINEARSLIDES) && (!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)))) + { + if (nFreqSlide < 0) + { + UINT n = (- nFreqSlide) >> 2; + if (n > 255) n = 255; + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideDownTable[n], 65536); + } else + { + UINT n = (nFreqSlide) >> 2; + + if (n > 255) n = 255; + pChn->nPeriod = _muldivr(pChn->nPeriod, LinearSlideUpTable[n], 65536); + } + } else + { + pChn->nPeriod += nFreqSlide; + } + if (pChn->nPeriod < 1) + { + pChn->nPeriod = 1; + if (m_nType & MOD_TYPE_IT) + { + pChn->dwFlags |= CHN_NOTEFADE; + pChn->nFadeOutVol = 0; + } + } +} + + +void CSoundFile::NoteCut(UINT nChn, UINT nTick) +//--------------------------------------------- +{ + if (m_nTickCount == nTick) + { + MODCHANNEL *pChn = &Chn[nChn]; + // if (m_nInstruments) KeyOff(pChn); ? + pChn->nVolume = 0; + pChn->dwFlags |= CHN_FASTVOLRAMP; + } +} + + +void CSoundFile::KeyOff(UINT nChn) +//-------------------------------- +{ + MODCHANNEL *pChn = &Chn[nChn]; + BOOL bKeyOn = (pChn->dwFlags & CHN_KEYOFF) ? FALSE : TRUE; + pChn->dwFlags |= CHN_KEYOFF; + //if ((!pChn->pHeader) || (!(pChn->dwFlags & CHN_VOLENV))) + if ((pChn->pHeader) && (!(pChn->dwFlags & CHN_VOLENV))) + { + pChn->dwFlags |= CHN_NOTEFADE; + } + if (!pChn->nLength) return; + if ((pChn->dwFlags & CHN_SUSTAINLOOP) && (pChn->pInstrument) && (bKeyOn)) + { + MODINSTRUMENT *psmp = pChn->pInstrument; + if (psmp->uFlags & CHN_LOOP) + { + if (psmp->uFlags & CHN_PINGPONGLOOP) + pChn->dwFlags |= CHN_PINGPONGLOOP; + else + pChn->dwFlags &= ~(CHN_PINGPONGLOOP|CHN_PINGPONGFLAG); + pChn->dwFlags |= CHN_LOOP; + pChn->nLength = psmp->nLength; + pChn->nLoopStart = psmp->nLoopStart; + pChn->nLoopEnd = psmp->nLoopEnd; + if (pChn->nLength > pChn->nLoopEnd) pChn->nLength = pChn->nLoopEnd; + } else + { + pChn->dwFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP|CHN_PINGPONGFLAG); + pChn->nLength = psmp->nLength; + } + } + if (pChn->pHeader) + { + INSTRUMENTHEADER *penv = pChn->pHeader; + if (((penv->dwFlags & ENV_VOLLOOP) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) && (penv->nFadeOut)) + pChn->dwFlags |= CHN_NOTEFADE; + } +} + + +////////////////////////////////////////////////////////// +// CSoundFile: Global Effects + + +void CSoundFile::SetSpeed(UINT param) +//----------------------------------- +{ + UINT max = (m_nType == MOD_TYPE_IT) ? 256 : 128; + // Modplug Tracker and Mod-Plugin don't do this check +#ifndef MODPLUG_TRACKER +#ifndef FASTSOUNDLIB + // Big Hack!!! + if ((!param) || (param >= 0x80) || ((m_nType & (MOD_TYPE_MOD|MOD_TYPE_XM|MOD_TYPE_MT2)) && (param >= 0x1E))) + { + if (IsSongFinished(m_nCurrentPattern, m_nRow+1)) + { + GlobalFadeSong(1000); + } + } +#endif // FASTSOUNDLIB +#endif // MODPLUG_TRACKER + if ((m_nType & MOD_TYPE_S3M) && (param > 0x80)) param -= 0x80; + if ((param) && (param <= max)) m_nMusicSpeed = param; +} + + +void CSoundFile::SetTempo(UINT param) +//----------------------------------- +{ + if (param < 0x20) + { + // Tempo Slide + if ((param & 0xF0) == 0x10) + { + m_nMusicTempo += (param & 0x0F) * 2; + if (m_nMusicTempo > 255) m_nMusicTempo = 255; + } else + { + m_nMusicTempo -= (param & 0x0F) * 2; + if ((LONG)m_nMusicTempo < 32) m_nMusicTempo = 32; + } + } else + { + m_nMusicTempo = param; + } +} + + +int CSoundFile::PatternLoop(MODCHANNEL *pChn, UINT param) +//------------------------------------------------------- +{ + if (param) + { + if (pChn->nPatternLoopCount) + { + pChn->nPatternLoopCount--; + if (!pChn->nPatternLoopCount) return -1; + } else + { + MODCHANNEL *p = Chn; + for (UINT i=0; i<m_nChannels; i++, p++) if (p != pChn) + { + // Loop already done + if (p->nPatternLoopCount) return -1; + } + pChn->nPatternLoopCount = param; + } + return pChn->nPatternLoop; + } else + { + pChn->nPatternLoop = m_nRow; + } + return -1; +} + + +void CSoundFile::GlobalVolSlide(UINT param) +//----------------------------------------- +{ + LONG nGlbSlide = 0; + if (param) m_nOldGlbVolSlide = param; else param = m_nOldGlbVolSlide; + if (((param & 0x0F) == 0x0F) && (param & 0xF0)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nGlbSlide = (param >> 4) * 2; + } else + if (((param & 0xF0) == 0xF0) && (param & 0x0F)) + { + if (m_dwSongFlags & SONG_FIRSTTICK) nGlbSlide = - (int)((param & 0x0F) * 2); + } else + { + if (!(m_dwSongFlags & SONG_FIRSTTICK)) + { + if (param & 0xF0) nGlbSlide = (int)((param & 0xF0) >> 4) * 2; + else nGlbSlide = -(int)((param & 0x0F) * 2); + } + } + if (nGlbSlide) + { + if (m_nType != MOD_TYPE_IT) nGlbSlide *= 2; + nGlbSlide += m_nGlobalVolume; + if (nGlbSlide < 0) nGlbSlide = 0; + if (nGlbSlide > 256) nGlbSlide = 256; + m_nGlobalVolume = nGlbSlide; + } +} + + +DWORD CSoundFile::IsSongFinished(UINT nStartOrder, UINT nStartRow) const +//---------------------------------------------------------------------- +{ + UINT nOrd; + + for (nOrd=nStartOrder; nOrd<MAX_ORDERS; nOrd++) + { + UINT nPat = Order[nOrd]; + if (nPat != 0xFE) + { + MODCOMMAND *p; + + if (nPat >= MAX_PATTERNS) break; + p = Patterns[nPat]; + if (p) + { + UINT len = PatternSize[nPat] * m_nChannels; + UINT pos = (nOrd == nStartOrder) ? nStartRow : 0; + pos *= m_nChannels; + while (pos < len) + { + UINT cmd; + if ((p[pos].note) || (p[pos].volcmd)) return 0; + cmd = p[pos].command; + if (cmd == CMD_MODCMDEX) + { + UINT cmdex = p[pos].param & 0xF0; + if ((!cmdex) || (cmdex == 0x60) || (cmdex == 0xE0) || (cmdex == 0xF0)) cmd = 0; + } + if ((cmd) && (cmd != CMD_SPEED) && (cmd != CMD_TEMPO)) return 0; + pos++; + } + } + } + } + return (nOrd < MAX_ORDERS) ? nOrd : MAX_ORDERS-1; +} + + +BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const +//---------------------------------------------------------------------------------------------------------- +{ + while ((nJumpOrder < MAX_PATTERNS) && (Order[nJumpOrder] == 0xFE)) nJumpOrder++; + if ((nStartOrder >= MAX_PATTERNS) || (nJumpOrder >= MAX_PATTERNS)) return FALSE; + // Treat only case with jumps in the same pattern + if (nJumpOrder > nStartOrder) return TRUE; + if ((nJumpOrder < nStartOrder) || (nJumpRow >= PatternSize[nStartOrder]) + || (!Patterns[nStartOrder]) || (nStartRow >= 256) || (nJumpRow >= 256)) return FALSE; + // See if the pattern is being played backward + BYTE row_hist[256]; + memset(row_hist, 0, sizeof(row_hist)); + UINT nRows = PatternSize[nStartOrder], row = nJumpRow; + if (nRows > 256) nRows = 256; + row_hist[nStartRow] = TRUE; + while ((row < 256) && (!row_hist[row])) + { + if (row >= nRows) return TRUE; + row_hist[row] = TRUE; + MODCOMMAND *p = Patterns[nStartOrder] + row * m_nChannels; + row++; + int breakrow = -1, posjump = 0; + for (UINT i=0; i<m_nChannels; i++, p++) + { + if (p->command == CMD_POSITIONJUMP) + { + if (p->param < nStartOrder) return FALSE; + if (p->param > nStartOrder) return TRUE; + posjump = TRUE; + } else + if (p->command == CMD_PATTERNBREAK) + { + breakrow = p->param; + } + } + if (breakrow >= 0) + { + if (!posjump) return TRUE; + row = breakrow; + } + if (row >= nRows) return TRUE; + } + return FALSE; +} + + +////////////////////////////////////////////////////// +// Note/Period/Frequency functions + +UINT CSoundFile::GetNoteFromPeriod(UINT period) const +//--------------------------------------------------- +{ + if (!period) return 0; + if (m_nType & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_MTM|MOD_TYPE_669|MOD_TYPE_OKT|MOD_TYPE_AMF0)) + { + period >>= 2; + for (UINT i=0; i<6*12; i++) + { + if (period >= ProTrackerPeriodTable[i]) + { + if ((period != ProTrackerPeriodTable[i]) && (i)) + { + UINT p1 = ProTrackerPeriodTable[i-1]; + UINT p2 = ProTrackerPeriodTable[i]; + if (p1 - period < (period - p2)) return i+36; + } + return i+1+36; + } + } + return 6*12+36; + } else + { + for (UINT i=1; i<120; i++) + { + LONG n = GetPeriodFromNote(i, 0, 0); + if ((n > 0) && (n <= (LONG)period)) return i; + } + return 120; + } +} + + + +UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const +//------------------------------------------------------------------------------- +{ + if ((!note) || (note > 0xF0)) return 0; + if (m_nType & (MOD_TYPE_IT|MOD_TYPE_S3M|MOD_TYPE_STM|MOD_TYPE_MDL|MOD_TYPE_ULT|MOD_TYPE_WAV + |MOD_TYPE_FAR|MOD_TYPE_DMF|MOD_TYPE_PTM|MOD_TYPE_AMS|MOD_TYPE_DBM|MOD_TYPE_AMF|MOD_TYPE_PSM)) + { + note--; + if (m_dwSongFlags & SONG_LINEARSLIDES) + { + return (FreqS3MTable[note % 12] << 5) >> (note / 12); + } else + { + if (!nC4Speed) nC4Speed = 8363; + return _muldiv(8363, (FreqS3MTable[note % 12] << 5), nC4Speed << (note / 12)); + } + } else + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (note < 13) note = 13; + note -= 13; + if (m_dwSongFlags & SONG_LINEARSLIDES) + { + LONG l = ((120 - note) << 6) - (nFineTune / 2); + if (l < 1) l = 1; + return (UINT)l; + } else + { + int finetune = nFineTune; + UINT rnote = (note % 12) << 3; + UINT roct = note / 12; + int rfine = finetune / 16; + int i = rnote + rfine + 8; + if (i < 0) i = 0; + if (i >= 104) i = 103; + UINT per1 = XMPeriodTable[i]; + if ( finetune < 0 ) + { + rfine--; + finetune = -finetune; + } else rfine++; + i = rnote+rfine+8; + if (i < 0) i = 0; + if (i >= 104) i = 103; + UINT per2 = XMPeriodTable[i]; + rfine = finetune & 0x0F; + per1 *= 16-rfine; + per2 *= rfine; + return ((per1 + per2) << 1) >> roct; + } + } else + { + note--; + nFineTune = XM2MODFineTune(nFineTune); + if ((nFineTune) || (note < 36) || (note >= 36+6*12)) + return (ProTrackerTunedPeriods[nFineTune*12 + note % 12] << 5) >> (note / 12); + else + return (ProTrackerPeriodTable[note-36] << 2); + } +} + + +UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const +//----------------------------------------------------------------------------------- +{ + if (!period) return 0; + if (m_nType & (MOD_TYPE_MED|MOD_TYPE_MOD|MOD_TYPE_MTM|MOD_TYPE_669|MOD_TYPE_OKT|MOD_TYPE_AMF0)) + { + return (3546895L*4) / period; + } else + if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) + { + if (m_dwSongFlags & SONG_LINEARSLIDES) + return XMLinearTable[period % 768] >> (period / 768); + else + return 8363 * 1712L / period; + } else + { + if (m_dwSongFlags & SONG_LINEARSLIDES) + { + if (!nC4Speed) nC4Speed = 8363; + return _muldiv(nC4Speed, 1712L << 8, (period << 8)+nPeriodFrac); + } else + { + return _muldiv(8363, 1712L << 8, (period << 8)+nPeriodFrac); + } + } +} + + |