|
From: <sag...@us...> - 2011-10-29 18:33:04
|
Revision: 1124
http://modplug.svn.sourceforge.net/modplug/?rev=1124&view=rev
Author: saga-games
Date: 2011-10-29 18:32:57 +0000 (Sat, 29 Oct 2011)
Log Message:
-----------
[Imp] Song position retrieval also works for sub tunes now, so song properties are restored properly (http://forum.openmpt.org/index.php?topic=4578.0).
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-10-25 21:09:00 UTC (rev 1123)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-10-29 18:32:57 UTC (rev 1124)
@@ -93,13 +93,15 @@
retval.lastOrder = retval.endOrder = ORDERINDEX_INVALID;
retval.lastRow = retval.endRow = ROWINDEX_INVALID;
+ // Are we trying to reach a certain pattern position?
+ const bool hasSearchTarget = (endOrder != ORDERINDEX_INVALID && endRow != ROWINDEX_INVALID);
+
// -> CODE#0022
// -> DESC="alternative BPM/Speed interpretation method"
// UINT dwElapsedTime=0, nRow=0, nCurrentPattern=0, nNextPattern=0, nPattern=Order[0];
ROWINDEX nRow = 0;
ROWINDEX nNextPatStartRow = 0; // FT2 E60 bug
- ORDERINDEX nCurrentPattern = 0;
- ORDERINDEX nNextPattern = 0;
+ ORDERINDEX nCurrentOrder = 0, nNextOrder = 0;
PATTERNINDEX nPattern = Order[0];
double dElapsedTime=0.0;
// -! NEW_FEATURE#0022
@@ -120,49 +122,68 @@
{
chnvols[icv] = ChnSettings[icv].nVolume;
}
- nCurrentPattern = nNextPattern = 0;
- nPattern = Order[0];
- nRow = nNextRow = 0;
for (;;)
{
UINT nSpeedCount = 0;
nRow = nNextRow;
- nCurrentPattern = nNextPattern;
+ nCurrentOrder = nNextOrder;
- if(nCurrentPattern >= Order.size())
+ if(nCurrentOrder >= Order.size())
break;
// Check if pattern is valid
- nPattern = Order[nCurrentPattern];
+ nPattern = Order[nCurrentOrder];
bool positionJumpOnThisRow = false;
bool patternBreakOnThisRow = false;
while(nPattern >= Patterns.Size())
{
// End of song?
- if((nPattern == Order.GetInvalidPatIndex()) || (nCurrentPattern >= Order.size()))
+ if((nPattern == Order.GetInvalidPatIndex()) || (nCurrentOrder >= Order.size()))
{
- if(nCurrentPattern == m_nRestartPos)
+ if(nCurrentOrder == m_nRestartPos)
break;
else
- nCurrentPattern = m_nRestartPos;
+ nCurrentOrder = m_nRestartPos;
} else
{
- nCurrentPattern++;
+ nCurrentOrder++;
}
- nPattern = (nCurrentPattern < Order.size()) ? Order[nCurrentPattern] : Order.GetInvalidPatIndex();
- nNextPattern = nCurrentPattern;
- if((!Patterns.IsValidPat(nPattern)) && IsRowVisited(nCurrentPattern, 0, true, &visitedRows))
- break;
+ nPattern = (nCurrentOrder < Order.size()) ? Order[nCurrentOrder] : Order.GetInvalidPatIndex();
+ nNextOrder = nCurrentOrder;
+ if((!Patterns.IsValidPat(nPattern)) && IsRowVisited(nCurrentOrder, 0, true, &visitedRows))
+ {
+ if(!hasSearchTarget || !GetFirstUnvisitedRow(nNextOrder, nNextRow, true, &visitedRows))
+ {
+ // We aren't searching for a specific row, or we couldn't find any more unvisited rows.
+ break;
+ } else
+ {
+ // We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
+ dElapsedTime = 0.0;
+ continue;
+ }
+ }
}
// Skip non-existing patterns
if ((nPattern >= Patterns.Size()) || (!Patterns[nPattern]))
{
// If there isn't even a tune, we should probably stop here.
- if(nCurrentPattern == m_nRestartPos)
- break;
- nNextPattern = nCurrentPattern + 1;
+ if(nCurrentOrder == m_nRestartPos)
+ {
+ if(!hasSearchTarget || !GetFirstUnvisitedRow(nNextOrder, nNextRow, true, &visitedRows))
+ {
+ // We aren't searching for a specific row, or we couldn't find any more unvisited rows.
+ break;
+ } else
+ {
+ // We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
+ dElapsedTime = 0.0;
+ continue;
+ }
+ }
+ nNextOrder = nCurrentOrder + 1;
continue;
}
// Should never happen
@@ -170,16 +191,27 @@
nRow = 0;
//Check whether target reached.
- if(nCurrentPattern == endOrder && nRow == endRow)
+ if(nCurrentOrder == endOrder && nRow == endRow)
{
retval.targetReached = true;
break;
}
- if(IsRowVisited(nCurrentPattern, nRow, true, &visitedRows))
- break;
+ if(IsRowVisited(nCurrentOrder, nRow, true, &visitedRows))
+ {
+ if(!hasSearchTarget || !GetFirstUnvisitedRow(nNextOrder, nNextRow, true, &visitedRows))
+ {
+ // We aren't searching for a specific row, or we couldn't find any more unvisited rows.
+ break;
+ } else
+ {
+ // We haven't found the target row yet, but we found some other unplayed row... continue searching from here.
+ dElapsedTime = 0.0;
+ continue;
+ }
+ }
- retval.endOrder = nCurrentPattern;
+ retval.endOrder = nCurrentOrder;
retval.endRow = nRow;
// Update next position
@@ -192,8 +224,8 @@
}
MODCHANNEL *pChn = Chn;
- MODCOMMAND *p = Patterns[nPattern] + nRow * m_nChannels;
- MODCOMMAND *nextRow = NULL;
+ MODCOMMAND *p = Patterns[nPattern].GetRow(nRow);
+ MODCOMMAND *nextRow = nullptr;
for (CHANNELINDEX nChn = 0; nChn < m_nChannels; p++, pChn++, nChn++) if (*((DWORD *)p))
{
if((GetType() == MOD_TYPE_S3M) && (ChnSettings[nChn].dwFlags & CHN_MUTE) != 0) // not even effects are processed on muted S3M channels
@@ -209,7 +241,7 @@
// Position Jump
case CMD_POSITIONJUMP:
positionJumpOnThisRow = true;
- nNextPattern = (ORDERINDEX)param;
+ nNextOrder = (ORDERINDEX)param;
nNextPatStartRow = 0; // FT2 E60 bug
// see http://forum.openmpt.org/index.php?topic=2769.0 - FastTracker resets Dxx if Bxx is called _after_ Dxx
if(!patternBreakOnThisRow || (GetType() == MOD_TYPE_XM))
@@ -246,7 +278,7 @@
if (!positionJumpOnThisRow)
{
- nNextPattern = nCurrentPattern + 1;
+ nNextOrder = nCurrentOrder + 1;
}
if ((adjustMode & eAdjust))
{
@@ -418,7 +450,7 @@
if (nNextRow >= Patterns[nPattern].GetNumRows())
{
- nNextPattern = nCurrentPattern + 1;
+ nNextOrder = nCurrentOrder + 1;
nNextRow = 0;
if(IsCompatibleMode(TRK_FASTTRACKER2)) nNextRow = nNextPatStartRow; // FT2 E60 bug
nNextPatStartRow = 0;
@@ -439,7 +471,7 @@
if(retval.targetReached || endOrder == ORDERINDEX_INVALID || endRow == ROWINDEX_INVALID)
{
- retval.lastOrder = nCurrentPattern;
+ retval.lastOrder = nCurrentOrder;
retval.lastRow = nRow;
}
retval.duration = dElapsedTime / 1000.0;
@@ -691,8 +723,8 @@
{
if (!pChn->nCutOff) pChn->nCutOff = 0x7F;
}
- if (pIns->nIFC & 0x80) pChn->nCutOff = pIns->nIFC & 0x7F;
- if (pIns->nIFR & 0x80) pChn->nResonance = pIns->nIFR & 0x7F;
+ if (pIns->IsCutoffEnabled()) pChn->nCutOff = pIns->GetCutoff();
+ if (pIns->IsResonanceEnabled()) pChn->nResonance = pIns->GetResonance();
}
pChn->nVolSwing = pChn->nPanSwing = 0;
pChn->nResSwing = pChn->nCutSwing = 0;
@@ -834,10 +866,10 @@
if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED))
{
note += pChn->nTranspose;
- note = CLAMP(note, NOTE_MIN, 131); // why 131? 120+11, how does this make sense?
+ Limit(note, NOTE_MIN, 131); // why 131? 120+11, how does this make sense?
} else
{
- note = CLAMP(note, NOTE_MIN, NOTE_MAX);
+ Limit(note, NOTE_MIN, NOTE_MAX);
}
if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
@@ -1001,12 +1033,12 @@
}
}
pChn->nLeftVol = pChn->nRightVol = 0;
- bool bFlt = (m_dwSongFlags & SONG_MPTFILTERMODE) ? false : true;
+ bool bFlt = (m_dwSongFlags & SONG_MPTFILTERMODE) == 0;
// Setup Initial Filter for this note
if (pIns)
{
- if (pIns->nIFR & 0x80) { pChn->nResonance = pIns->nIFR & 0x7F; bFlt = true; }
- if (pIns->nIFC & 0x80) { pChn->nCutOff = pIns->nIFC & 0x7F; bFlt = true; }
+ if (pIns->IsResonanceEnabled()) { pChn->nResonance = pIns->GetResonance(); bFlt = true; }
+ if (pIns->IsResonanceEnabled()) { pChn->nCutOff = pIns->GetCutoff(); bFlt = true; }
if (bFlt && (pIns->nFilterMode != FLTMODE_UNCHANGED))
{
pChn->nFilterMode = pIns->nFilterMode;
@@ -1427,6 +1459,7 @@
if ((((param & 0xF0) == 0x60) && (cmd == CMD_MODCMDEX))
|| (((param & 0xF0) == 0xB0) && (cmd == CMD_S3MCMDEX)))
{
+
ROWINDEX nloop = PatternLoop(pChn, param & 0x0F);
if (nloop != ROWINDEX_INVALID) nPatLoopRow = nloop;
@@ -3648,7 +3681,7 @@
if (param < 0x100) bResetEnv = true;
}
// IT compatibility: Really weird combination of envelopes and retrigger (see Storlek's q.it testcase)
- NoteChange(nChn, nNote, IsCompatibleMode(TRK_IMPULSETRACKER) ? true : false, bResetEnv);
+ NoteChange(nChn, nNote, IsCompatibleMode(TRK_IMPULSETRACKER), bResetEnv);
if (m_nInstruments)
{
ProcessMidiOut(nChn, pChn); //Send retrig to Midi
@@ -4206,7 +4239,7 @@
// Unlike channel settings, pModInstrument is copied from the original chan to the NNA chan,
// so we don't need to worry about finding the master chan.
- UINT nPlugin=0;
+ PLUGINDEX nPlugin = 0;
if (pChn && pChn->pModInstrument)
{
if (respectMutes == RespectMutes && pChn->pModSample && (pChn->pModSample->uFlags & CHN_MUTE))
@@ -4332,8 +4365,8 @@
// Resize / Clear the row vector.
// If bReset is true, the vector is not only resized to the required dimensions, but also completely cleared (i.e. all visited rows are unset).
// If pRowVector is specified, an alternative row vector instead of the module's global one will be used (f.e. when using GetLength()).
-void CSoundFile::InitializeVisitedRows(const bool bReset, VisitedRowsType *pRowVector)
-//------------------------------------------------------------------------------------
+void CSoundFile::InitializeVisitedRows(bool bReset, VisitedRowsType *pRowVector)
+//------------------------------------------------------------------------------
{
if(pRowVector == nullptr)
{
@@ -4358,8 +4391,8 @@
// nOrd, nRow - which row should be (un)set
// If bVisited is true, the row will be set as visited.
// If pRowVector is specified, an alternative row vector instead of the module's global one will be used (f.e. when using GetLength()).
-void CSoundFile::SetRowVisited(const ORDERINDEX nOrd, const ROWINDEX nRow, const bool bVisited, VisitedRowsType *pRowVector)
-//--------------------------------------------------------------------------------------------------------------------------
+void CSoundFile::SetRowVisited(ORDERINDEX nOrd, ROWINDEX nRow, bool bVisited, VisitedRowsType *pRowVector)
+//--------------------------------------------------------------------------------------------------------
{
const ORDERINDEX nMaxOrd = Order.GetLengthTailTrimmed();
if(nOrd >= nMaxOrd || nRow >= GetVisitedRowsVectorSize(Order[nOrd]))
@@ -4386,8 +4419,8 @@
// If bAutoSet is true, the queried row will automatically be marked as visited.
// Use this parameter instead of consecutive IsRowVisited/SetRowVisited calls.
// If pRowVector is specified, an alternative row vector instead of the module's global one will be used (f.e. when using GetLength()).
-bool CSoundFile::IsRowVisited(const ORDERINDEX nOrd, const ROWINDEX nRow, const bool bAutoSet, VisitedRowsType *pRowVector)
-//-------------------------------------------------------------------------------------------------------------------------
+bool CSoundFile::IsRowVisited(ORDERINDEX nOrd, ROWINDEX nRow, bool bAutoSet, VisitedRowsType *pRowVector)
+//-------------------------------------------------------------------------------------------------------
{
const ORDERINDEX nMaxOrd = Order.GetLengthTailTrimmed();
if(nOrd >= nMaxOrd)
@@ -4426,8 +4459,8 @@
// Get the needed vector size for pattern nPat.
-size_t CSoundFile::GetVisitedRowsVectorSize(const PATTERNINDEX nPat) const
-//------------------------------------------------------------------------
+size_t CSoundFile::GetVisitedRowsVectorSize(PATTERNINDEX nPat) const
+//------------------------------------------------------------------
{
if(Patterns.IsValidPat(nPat))
{
@@ -4440,3 +4473,47 @@
}
}
+
+// Find the first row that has not been played yet.
+// The order and row is stored in the order and row variables on success, on failure they contain invalid values.
+// If fastSearch is true (default), only the first row of each pattern is looked at, otherwise every row is examined.
+// Function returns true on success.
+bool CSoundFile::GetFirstUnvisitedRow(ORDERINDEX &order, ROWINDEX &row, bool fastSearch, const VisitedRowsType *pRowVector) const
+//-------------------------------------------------------------------------------------------------------------------------------
+{
+ if(pRowVector == nullptr)
+ {
+ pRowVector = &m_VisitedRows;
+ }
+
+ const ORDERINDEX endOrder = Order.GetLengthTailTrimmed();
+ for(order = 0; order < endOrder; order++)
+ {
+ const PATTERNINDEX pattern = Order[order];
+ if(!Patterns.IsValidPat(pattern))
+ {
+ continue;
+ }
+
+ if(order >= pRowVector->size())
+ {
+ // Not yet initialized => unvisited
+ return true;
+ }
+
+ const ROWINDEX endRow = (fastSearch ? 1 : Patterns[pattern].GetNumRows());
+ for(row = 0; row < endRow; row++)
+ {
+ if(row >= pRowVector->at(order).size() || pRowVector->at(order).at(row) == false)
+ {
+ // Not yet initialized, or unvisited
+ return true;
+ }
+ }
+ }
+
+ // Didn't find anything :(
+ order = ORDERINDEX_INVALID;
+ row = ROWINDEX_INVALID;
+ return false;
+}
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2011-10-25 21:09:00 UTC (rev 1123)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2011-10-29 18:32:57 UTC (rev 1124)
@@ -115,31 +115,31 @@
UINT nGlobalVol; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker)
UINT nPan; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning.
- BYTE nNNA; // New note action
- BYTE nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action)
- BYTE nDNA; // Duplicate note action
- BYTE nPanSwing; // Random panning factor (0...64)
- BYTE nVolSwing; // Random volume factor (0...100)
- BYTE nIFC; // Default filter cutoff (0...127). Used if the high bit is set
- BYTE nIFR; // Default filter resonance (0...127). Used if the high bit is set
+ uint8 nNNA; // New note action
+ uint8 nDCT; // Duplicate check type (i.e. which condition will trigger the duplicate note action)
+ uint8 nDNA; // Duplicate note action
+ uint8 nPanSwing; // Random panning factor (0...64)
+ uint8 nVolSwing; // Random volume factor (0...100)
+ uint8 nIFC; // Default filter cutoff (0...127). Used if the high bit is set
+ uint8 nIFR; // Default filter resonance (0...127). Used if the high bit is set
- WORD wMidiBank; // MIDI Bank (1...16384). 0 = Don't send.
- BYTE nMidiProgram; // MIDI Program (1...128). 0 = Don't send.
- BYTE nMidiChannel; // MIDI Channel (1...16). 0 = Don't send.
- BYTE nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader)
+ uint16 wMidiBank; // MIDI Bank (1...16384). 0 = Don't send.
+ uint8 nMidiProgram; // MIDI Program (1...128). 0 = Don't send.
+ uint8 nMidiChannel; // MIDI Channel (1...16). 0 = Don't send.
+ uint8 nMidiDrumKey; // Drum set note mapping (currently only used by the .MID loader)
- signed char nPPS; //Pitch/Pan separation (i.e. how wide the panning spreads)
- unsigned char nPPC; //Pitch/Pan centre
+ int8 nPPS; //Pitch/Pan separation (i.e. how wide the panning spreads)
+ uint8 nPPC; //Pitch/Pan centre
PLUGINDEX nMixPlug; // Plugin assigned to this instrument
uint16 nVolRampUp; // Default sample ramping up
UINT nResampling; // Resampling mode
- BYTE nCutSwing; // Random cutoff factor (0...64)
- BYTE nResSwing; // Random resonance factor (0...64)
- BYTE nFilterMode; // Default filter mode
- WORD wPitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly
- BYTE nPluginVelocityHandling; // How to deal with plugin velocity
- BYTE nPluginVolumeHandling; // How to deal with plugin volume
+ uint8 nCutSwing; // Random cutoff factor (0...64)
+ uint8 nResSwing; // Random resonance factor (0...64)
+ uint8 nFilterMode; // Default filter mode
+ uint16 wPitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly
+ uint8 nPluginVelocityHandling; // How to deal with plugin velocity
+ uint8 nPluginVolumeHandling; // How to deal with plugin volume
CTuning *pTuning; // sample tuning assigned to this instrument
static CTuning *s_DefaultTuning;
@@ -175,8 +175,8 @@
nPanSwing = 0;
nVolSwing = 0;
- nIFC = 0;
- nIFR = 0;
+ SetCutoff(0, false);
+ SetResonance(0, false);
wMidiBank = 0;
nMidiProgram = 0;
@@ -208,6 +208,13 @@
MemsetZero(filename);
}
+ bool IsCutoffEnabled() const { return (nIFC & 0x80) != 0; }
+ bool IsResonanceEnabled() const { return (nIFR & 0x80) != 0; }
+ uint8 GetCutoff() const { return (nIFC & 0x7F); }
+ uint8 GetResonance() const { return (nIFR & 0x7F); }
+ void SetCutoff(uint8 cutoff, bool enable) { nIFC = min(cutoff, 0x7F) | (enable ? 0x80 : 0x00); }
+ void SetResonance(uint8 resonance, bool enable) { nIFR = min(resonance, 0x7F) | (enable ? 0x80 : 0x00); }
+
bool HasValidMIDIChannel() const { return (nMidiChannel >= 1 && nMidiChannel <= 16); }
};
@@ -446,7 +453,7 @@
struct SNDMIXPLUGIN
{
- const char* GetName() const {return Info.szName;}
+ const char* GetName() const { return Info.szName; }
const char* GetLibraryName();
CString GetParamName(const UINT index) const;
bool Bypass(bool bypass);
@@ -631,7 +638,7 @@
{
if(GetType() & type & (MOD_TYPE_MOD | MOD_TYPE_S3M))
return true; // S3M and MOD format don't have compatibility flags, so we will always return true
- return ((GetType() & type) && GetModFlag(MSF_COMPATIBLE_PLAY)) ? true : false;
+ return ((GetType() & type) && GetModFlag(MSF_COMPATIBLE_PLAY));
}
// Check whether a filter algorithm closer to IT's should be used.
@@ -810,7 +817,7 @@
// A repeat count value of -1 means infinite loop
void SetRepeatCount(int n) { m_nRepeatCount = n; }
int GetRepeatCount() const { return m_nRepeatCount; }
- bool IsPaused() const { return (m_dwSongFlags & (SONG_PAUSED|SONG_STEP)) ? true : false; } // Added SONG_STEP as it seems to be desirable in most cases to check for this as well.
+ bool IsPaused() const { return (m_dwSongFlags & (SONG_PAUSED|SONG_STEP)) != 0; } // Added SONG_STEP as it seems to be desirable in most cases to check for this as well.
void LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow = 0);
void CheckCPUUsage(UINT nCPU);
@@ -1172,14 +1179,15 @@
public:
void InitializeVisitedRows(const bool bReset = true, VisitedRowsType *pRowVector = nullptr);
private:
- void SetRowVisited(const ORDERINDEX nOrd, const ROWINDEX nRow, const bool bVisited = true, VisitedRowsType *pRowVector = nullptr);
- bool IsRowVisited(const ORDERINDEX nOrd, const ROWINDEX nRow, const bool bAutoSet = true, VisitedRowsType *pRowVector = nullptr);
- size_t GetVisitedRowsVectorSize(const PATTERNINDEX nPat) const;
+ void SetRowVisited(ORDERINDEX nOrd, ROWINDEX nRow, bool bVisited = true, VisitedRowsType *pRowVector = nullptr);
+ bool IsRowVisited(ORDERINDEX nOrd, ROWINDEX nRow, bool bAutoSet = true, VisitedRowsType *pRowVector = nullptr);
+ size_t GetVisitedRowsVectorSize(PATTERNINDEX nPat) const;
+ bool GetFirstUnvisitedRow(ORDERINDEX &order, ROWINDEX &row, bool fastSearch = true, const VisitedRowsType *pRowVector = nullptr) const;
public:
// "importance" of every FX command. Table is used for importing from formats with multiple effect columns
// and is approximately the same as in SchismTracker.
- static size_t CSoundFile::GetEffectWeight(MODCOMMAND::COMMAND cmd);
+ static size_t GetEffectWeight(MODCOMMAND::COMMAND cmd);
// try to convert a an effect into a volume column effect.
static bool ConvertVolEffect(uint8 *e, uint8 *p, bool bForce);
};
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|