Mercurial > audlegacy-plugins
view src/modplug/snd_fx.cxx @ 2284:d19b53359b24
cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV
PLAYBACK. if somebody is more experienced with it and wants to restore
the other formats, go ahead (maybe change the name of the plugin too?).
author | mf0102 <0102@gmx.at> |
---|---|
date | Wed, 09 Jan 2008 15:41:22 +0100 |
parents | 6907fc39b53f |
children | 107c1fed3d92 |
line wrap: on
line source
/* * 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 MODPLUG_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 // MODPLUG_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 MODPLUG_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 // MODPLUG_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); } } }