|
From: <sag...@us...> - 2011-09-19 00:34:43
|
Revision: 1044
http://modplug.svn.sourceforge.net/modplug/?rev=1044&view=rev
Author: saga-games
Date: 2011-09-19 00:34:36 +0000 (Mon, 19 Sep 2011)
Log Message:
-----------
[Fix] Revision 1034 severely broke global volume ramping (rapid changes suddenly became very smooth)
Revision Links:
--------------
http://modplug.svn.sourceforge.net/modplug/?rev=1034&view=rev
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/Sndmix.cpp
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2011-09-18 23:40:27 UTC (rev 1043)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2011-09-19 00:34:36 UTC (rev 1044)
@@ -737,7 +737,8 @@
m_nGlobalVolume = m_nDefaultGlobalVolume;
m_lHighResRampingGlobalVolume = m_nGlobalVolume<<VOLUMERAMPPRECISION;
m_nGlobalVolumeDestination = m_nGlobalVolume;
- m_nSamplesToGlobalVolRampDest=0;
+ m_nSamplesToGlobalVolRampDest = 0;
+ m_nGlobalVolumeRampAmount = 0;
m_nNextPattern = 0;
m_nCurrentPattern = 0;
m_nPattern = 0;
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2011-09-18 23:40:27 UTC (rev 1043)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2011-09-19 00:34:36 UTC (rev 1044)
@@ -711,7 +711,7 @@
ORDERINDEX m_nCurrentPattern, m_nNextPattern, m_nRestartPos, m_nSeqOverride;
//NOTE: m_nCurrentPattern and m_nNextPattern refer to order index - not pattern index.
bool m_bPatternTransitionOccurred;
- UINT m_nMasterVolume, m_nGlobalVolume, m_nSamplesToGlobalVolRampDest,
+ UINT m_nMasterVolume, m_nGlobalVolume, m_nSamplesToGlobalVolRampDest, m_nGlobalVolumeRampAmount,
m_nGlobalVolumeDestination, m_nSamplePreAmp, m_nVSTiVolume;
long m_lHighResRampingGlobalVolume;
UINT m_nFreqFactor, m_nTempoFactor, m_nOldGlbVolSlide;
Modified: trunk/OpenMPT/soundlib/Sndmix.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndmix.cpp 2011-09-18 23:40:27 UTC (rev 1043)
+++ trunk/OpenMPT/soundlib/Sndmix.cpp 2011-09-19 00:34:36 UTC (rev 1044)
@@ -2572,11 +2572,9 @@
// User has provided new global volume
const bool rampUp = m_nGlobalVolumeDestination > m_nGlobalVolume;
m_nGlobalVolumeDestination = m_nGlobalVolume;
- m_nSamplesToGlobalVolRampDest = rampUp ? gnVolumeRampUpSamples : gnVolumeRampDownSamples;
+ m_nSamplesToGlobalVolRampDest = m_nGlobalVolumeRampAmount = rampUp ? gnVolumeRampUpSamples : gnVolumeRampDownSamples;
}
- const long rampLength = m_nSamplesToGlobalVolRampDest;
-
if (m_nSamplesToGlobalVolRampDest > 0)
{
// Still some ramping left to do.
@@ -2585,10 +2583,12 @@
const long delta = highResGlobalVolumeDestination - m_lHighResRampingGlobalVolume;
step = delta / static_cast<long>(m_nSamplesToGlobalVolRampDest);
- UINT maxStep = max(50, (10000 / (rampLength + 1))); // Define max step size as some factor of user defined ramping value: the lower the value, the more likely the click.
- while(abs(step) > maxStep) // If step is too big (might cause click), extend ramp length.
+ // Define max step size as some factor of user defined ramping value: the lower the value, the more likely the click.
+ // If step is too big (might cause click), extend ramp length.
+ UINT maxStep = max(50, (10000 / (m_nGlobalVolumeRampAmount + 1)));
+ while(static_cast<UINT>(abs(step)) > maxStep)
{
- m_nSamplesToGlobalVolRampDest += rampLength;
+ m_nSamplesToGlobalVolRampDest += m_nGlobalVolumeRampAmount;
step = delta / static_cast<long>(m_nSamplesToGlobalVolRampDest);
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2011-09-28 20:53:27
|
Revision: 1063
http://modplug.svn.sourceforge.net/modplug/?rev=1063&view=rev
Author: saga-games
Date: 2011-09-28 20:53:21 +0000 (Wed, 28 Sep 2011)
Log Message:
-----------
[Ref] MODCHANNEL: nRowNote, nRowInstr, nRowVolCmd, nRowVolume, nRowCommand, nRowParam => rowCommand, m_plugInitialParamValue => m_plugParamTargetValue
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/Sndmix.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-09-28 20:37:39 UTC (rev 1062)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-09-28 20:53:21 UTC (rev 1063)
@@ -902,7 +902,7 @@
if ((!bPorta) || (!(m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT)))
|| ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol))
- || ((m_dwSongFlags & SONG_ITCOMPATGXX) && (pChn->nRowInstr)))
+ || ((m_dwSongFlags & SONG_ITCOMPATGXX) && (pChn->rowCommand.instr)))
{
if ((m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT)) && (pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol))
{
@@ -916,9 +916,9 @@
pChn->dwFlags &= ~CHN_NOTEFADE;
pChn->nFadeOutVol = 65536;
}
- if ((!bPorta) || (!(m_dwSongFlags & SONG_ITCOMPATGXX)) || (pChn->nRowInstr))
+ if ((!bPorta) || (!(m_dwSongFlags & SONG_ITCOMPATGXX)) || (pChn->rowCommand.instr))
{
- if ((!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (pChn->nRowInstr))
+ if ((!(m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) || (pChn->rowCommand.instr))
{
pChn->dwFlags &= ~CHN_NOTEFADE;
pChn->nFadeOutVol = 65536;
@@ -1301,11 +1301,11 @@
// -! NEW_FEATURE#0010
for (CHANNELINDEX 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;
+ UINT instr = pChn->rowCommand.instr;
+ UINT volcmd = pChn->rowCommand.volcmd;
+ UINT vol = pChn->rowCommand.vol;
+ UINT cmd = pChn->rowCommand.command;
+ UINT param = pChn->rowCommand.param;
bool bPorta = ((cmd != CMD_TONEPORTAMENTO) && (cmd != CMD_TONEPORTAVOL) && (volcmd != VOLCMD_TONEPORTAMENTO)) ? false : true;
UINT nStartTick = 0;
@@ -1313,11 +1313,11 @@
pChn->dwFlags &= ~CHN_FASTVOLRAMP;
// Process parameter control note.
- if(pChn->nRowNote == NOTE_PC)
+ if(pChn->rowCommand.note == NOTE_PC)
{
- const PLUGINDEX plug = pChn->nRowInstr;
- const PlugParamIndex plugparam = MODCOMMAND::GetValueVolCol(pChn->nRowVolCmd, pChn->nRowVolume);
- const PlugParamValue value = MODCOMMAND::GetValueEffectCol(pChn->nRowCommand, pChn->nRowParam) / PlugParamValue(MODCOMMAND::maxColumnValue);
+ const PLUGINDEX plug = pChn->rowCommand.instr;
+ const PlugParamIndex plugparam = MODCOMMAND::GetValueVolCol(pChn->rowCommand.volcmd, pChn->rowCommand.vol);
+ const PlugParamValue value = MODCOMMAND::GetValueEffectCol(pChn->rowCommand.command, pChn->rowCommand.param) / PlugParamValue(MODCOMMAND::maxColumnValue);
if(plug > 0 && plug <= MAX_MIXPLUGINS && m_MixPlugins[plug-1].pMixPlugin)
m_MixPlugins[plug-1].pMixPlugin->SetParameter(plugparam, value);
@@ -1329,30 +1329,27 @@
// the need for parameter control. The condition cmd == 0
// is to make sure that m_nPlugParamValueStep != 0 because
// of NOTE_PCS, not because of macro.
- if(pChn->nRowNote == NOTE_PCS || (cmd == 0 && pChn->m_plugParamValueStep != 0))
+ if(pChn->rowCommand.note == NOTE_PCS || (cmd == CMD_NONE && pChn->m_plugParamValueStep != 0))
{
const bool isFirstTick = (m_dwSongFlags & SONG_FIRSTTICK) != 0;
if(isFirstTick)
- pChn->m_RowPlug = pChn->nRowInstr;
+ pChn->m_RowPlug = pChn->rowCommand.instr;
const PLUGINDEX nPlug = pChn->m_RowPlug;
const bool hasValidPlug = (nPlug > 0 && nPlug <= MAX_MIXPLUGINS && m_MixPlugins[nPlug-1].pMixPlugin);
if(hasValidPlug)
{
if(isFirstTick)
- pChn->m_RowPlugParam = MODCOMMAND::GetValueVolCol(pChn->nRowVolCmd, pChn->nRowVolume);
+ pChn->m_RowPlugParam = MODCOMMAND::GetValueVolCol(pChn->rowCommand.volcmd, pChn->rowCommand.vol);
const PlugParamIndex plugparam = pChn->m_RowPlugParam;
if(isFirstTick)
{
- PlugParamValue targetvalue = MODCOMMAND::GetValueEffectCol(pChn->nRowCommand, pChn->nRowParam) / PlugParamValue(MODCOMMAND::maxColumnValue);
- // Hack: Use m_nPlugInitialParamValue to store the target value, not initial.
- pChn->m_plugInitialParamValue = targetvalue;
- pChn->m_plugParamValueStep = (targetvalue - m_MixPlugins[nPlug-1].pMixPlugin->GetParameter(plugparam)) / float(m_nMusicSpeed);
+ PlugParamValue targetvalue = MODCOMMAND::GetValueEffectCol(pChn->rowCommand.command, pChn->rowCommand.param) / PlugParamValue(MODCOMMAND::maxColumnValue);
+ pChn->m_plugParamTargetValue = targetvalue;
+ pChn->m_plugParamValueStep = (targetvalue - m_MixPlugins[nPlug-1].pMixPlugin->GetParameter(plugparam)) / float(GetNumTicksOnCurrentRow());
}
if(m_nTickCount + 1 == GetNumTicksOnCurrentRow())
{ // On last tick, set parameter exactly to target value.
- // Note: m_nPlugInitialParamValue is used to store the target value,
- // not the initial value as the name suggests.
- m_MixPlugins[nPlug-1].pMixPlugin->SetParameter(plugparam, pChn->m_plugInitialParamValue);
+ m_MixPlugins[nPlug-1].pMixPlugin->SetParameter(plugparam, pChn->m_plugParamTargetValue);
}
else
m_MixPlugins[nPlug-1].pMixPlugin->ModifyParameter(plugparam, pChn->m_plugParamValueStep);
@@ -1361,7 +1358,7 @@
// Apart from changing parameters, parameter control notes are intended to be 'invisible'.
// To achieve this, clearing the note data so that rest of the process sees the row as empty row.
- if(MODCOMMAND::IsPcNote(pChn->nRowNote))
+ if(MODCOMMAND::IsPcNote(pChn->rowCommand.note))
{
pChn->ClearRowCmd();
instr = 0;
@@ -1438,7 +1435,7 @@
// Handles note/instrument/volume changes
if (m_nTickCount == nStartTick) // can be delayed by a note delay effect
{
- UINT note = pChn->nRowNote;
+ UINT note = pChn->rowCommand.note;
if (instr) pChn->nNewIns = instr;
bool retrigEnv = (!note) && (instr);
@@ -2965,7 +2962,7 @@
case 0xA0: if(m_dwSongFlags & SONG_FIRSTTICK)
{
pChn->nOldHiOffset = param;
- if (!IsCompatibleMode(TRK_IMPULSETRACKER) && (pChn->nRowNote != NOTE_NONE) && NOTE_IS_VALID(pChn->nRowNote))
+ if (!IsCompatibleMode(TRK_IMPULSETRACKER) && (pChn->rowCommand.note != NOTE_NONE) && NOTE_IS_VALID(pChn->rowCommand.note))
{
DWORD pos = param << 16;
if (pos < pChn->nLength) pChn->nPos = pos;
@@ -3451,7 +3448,7 @@
}
// -! NEW_FEATURE#0010
- if ((pChn->nRowNote >= NOTE_MIN) && (pChn->nRowNote <= NOTE_MAX))
+ if ((pChn->rowCommand.note >= NOTE_MIN) && (pChn->rowCommand.note <= NOTE_MAX))
{
// XM compatibility: Portamento + Offset = Ignore offset
if(bPorta && IsCompatibleMode(TRK_FASTTRACKER2))
@@ -3510,7 +3507,7 @@
//IT compatibility 15. Retrigger
if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
- if ((m_dwSongFlags & SONG_FIRSTTICK) && pChn->nRowNote)
+ if ((m_dwSongFlags & SONG_FIRSTTICK) && pChn->rowCommand.note)
{
pChn->nRetrigCount = param & 0xf;
}
@@ -3525,13 +3522,13 @@
if(m_dwSongFlags & SONG_FIRSTTICK)
{
// here are some really stupid things FT2 does
- if(pChn->nRowVolCmd == VOLCMD_VOLUME) return;
- if(pChn->nRowInstr > 0 && pChn->nRowNote == NOTE_NONE) nRetrigCount = 1;
- if(pChn->nRowNote != NOTE_NONE && pChn->nRowNote <= GetModSpecifications().noteMax) nRetrigCount++;
+ if(pChn->rowCommand.volcmd == VOLCMD_VOLUME) return;
+ if(pChn->rowCommand.instr > 0 && pChn->rowCommand.note == NOTE_NONE) nRetrigCount = 1;
+ if(pChn->rowCommand.note != NOTE_NONE && pChn->rowCommand.note <= GetModSpecifications().noteMax) nRetrigCount++;
}
if (nRetrigCount >= nRetrigSpeed)
{
- if (!(m_dwSongFlags & SONG_FIRSTTICK) || (pChn->nRowNote == NOTE_NONE))
+ if (!(m_dwSongFlags & SONG_FIRSTTICK) || (pChn->rowCommand.note == NOTE_NONE))
{
bDoRetrig = true;
nRetrigCount = 0;
@@ -3549,7 +3546,7 @@
{
int realspeed = nRetrigSpeed;
// FT2 bug: if a retrig (Rxy) occours together with a volume command, the first retrig interval is increased by one tick
- if ((param & 0x100) && (pChn->nRowVolCmd == VOLCMD_VOLUME) && (pChn->nRowParam & 0xF0)) realspeed++;
+ if ((param & 0x100) && (pChn->rowCommand.volcmd == VOLCMD_VOLUME) && (pChn->rowCommand.param & 0xF0)) realspeed++;
if (!(m_dwSongFlags & SONG_FIRSTTICK) || (param & 0x100))
{
if (!realspeed) realspeed = 1;
@@ -3558,7 +3555,7 @@
} 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 ((m_nTickCount) || ((param & 0x100) && (!pChn->rowCommand.note))) bDoRetrig = true;
}
}
}
@@ -3571,7 +3568,7 @@
int vol = pChn->nVolume;
// XM compatibility: Retrig + volume will not change volume of retrigged notes
- if(!IsCompatibleMode(TRK_FASTTRACKER2) || !(pChn->nRowVolCmd == VOLCMD_VOLUME))
+ if(!IsCompatibleMode(TRK_FASTTRACKER2) || !(pChn->rowCommand.volcmd == VOLCMD_VOLUME))
{
if (retrigTable1[dv])
vol = (vol * retrigTable1[dv]) >> 4;
@@ -3590,9 +3587,9 @@
bool bResetEnv = false;
if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))
{
- if ((pChn->nRowInstr) && (param < 0x100))
+ if ((pChn->rowCommand.instr) && (param < 0x100))
{
- InstrumentChange(pChn, pChn->nRowInstr, false, false);
+ InstrumentChange(pChn, pChn->rowCommand.instr, false, false);
bResetEnv = true;
}
if (param < 0x100) bResetEnv = true;
@@ -3603,7 +3600,7 @@
{
ProcessMidiOut(nChn, pChn); //Send retrig to Midi
}
- if ((m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT)) && (!pChn->nRowNote) && (nOldPeriod)) pChn->nPeriod = nOldPeriod;
+ if ((m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT)) && (!pChn->rowCommand.note) && (nOldPeriod)) pChn->nPeriod = nOldPeriod;
if (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) nRetrigCount = 0;
// IT compatibility: see previous IT compatibility comment =)
if(IsCompatibleMode(TRK_IMPULSETRACKER)) pChn->nPos = pChn->nPosLo = 0;
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2011-09-28 20:37:39 UTC (rev 1062)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2011-09-28 20:53:21 UTC (rev 1063)
@@ -301,19 +301,18 @@
BYTE nCutOff, nResonance;
BYTE nTremorCount, nTremorParam;
BYTE nPatternLoopCount;
- BYTE nRowNote, nRowInstr;
- BYTE nRowVolCmd, nRowVolume;
- BYTE nRowCommand, nRowParam;
BYTE nLeftVU, nRightVU;
BYTE nActiveMacro, nFilterMode;
BYTE nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only)
+ MODCOMMAND rowCommand;
+
//NOTE_PCs memory.
uint16 m_RowPlugParam;
- float m_plugParamValueStep, m_plugInitialParamValue;
+ float m_plugParamValueStep, m_plugParamTargetValue;
PLUGINDEX m_RowPlug;
- void ClearRowCmd() {nRowNote = NOTE_NONE; nRowInstr = 0; nRowVolCmd = VOLCMD_NONE; nRowVolume = 0; nRowCommand = CMD_NONE; nRowParam = 0;}
+ void ClearRowCmd() { rowCommand = MODCOMMAND::Empty(); }
typedef UINT VOLUME;
VOLUME GetVSTVolume() {return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume;}
Modified: trunk/OpenMPT/soundlib/Sndmix.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndmix.cpp 2011-09-28 20:37:39 UTC (rev 1062)
+++ trunk/OpenMPT/soundlib/Sndmix.cpp 2011-09-28 20:53:21 UTC (rev 1063)
@@ -851,7 +851,7 @@
m_nNextRow = 0;
// FT2 idiosyncrasy: When E60 is used on a pattern row x, the following pattern also starts from row x
- // instead of the beginning of the pattern, unless there was a Dxx or Cxx effect.
+ // instead of the beginning of the pattern, unless there was a Bxx or Dxx effect.
if(IsCompatibleMode(TRK_FASTTRACKER2))
{
m_nNextRow = m_nNextPatStartRow;
@@ -863,12 +863,7 @@
MODCOMMAND *m = Patterns[m_nPattern].GetRow(m_nRow);
for (CHANNELINDEX nChn=0; nChn<m_nChannels; pChn++, nChn++, m++)
{
- pChn->nRowNote = m->note;
- pChn->nRowInstr = m->instr;
- pChn->nRowVolCmd = m->volcmd;
- pChn->nRowVolume = m->vol;
- pChn->nRowCommand = m->command;
- pChn->nRowParam = m->param;
+ pChn->rowCommand = *m;
pChn->nLeftVol = pChn->nNewLeftVol;
pChn->nRightVol = pChn->nNewRightVol;
@@ -918,26 +913,22 @@
void CSoundFile::ProcessVolumeSwing(MODCHANNEL *pChn, int &vol)
//-------------------------------------------------------------
{
- if(pChn->nVolSwing)
+ if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
- if(IsCompatibleMode(TRK_IMPULSETRACKER))
- {
- vol += pChn->nVolSwing;
- Limit(vol, 0, 64);
- } else if(GetModFlag(MSF_OLDVOLSWING))
- {
- vol += pChn->nVolSwing;
- Limit(vol, 0, 256);
- }
- else
- {
- pChn->nVolume += pChn->nVolSwing;
- pChn->nVolume = CLAMP(pChn->nVolume, 0, 256);
- vol = pChn->nVolume;
- pChn->nVolSwing = 0;
- Limit(vol, 0, 256);
- }
+ vol += pChn->nVolSwing;
+ Limit(vol, 0, 64);
+ } else if(GetModFlag(MSF_OLDVOLSWING))
+ {
+ vol += pChn->nVolSwing;
+ Limit(vol, 0, 256);
}
+ else
+ {
+ pChn->nVolume += pChn->nVolSwing;
+ Limit(pChn->nVolume, 0, 256);
+ vol = pChn->nVolume;
+ pChn->nVolSwing = 0;
+ }
}
@@ -947,6 +938,7 @@
if(IsCompatibleMode(TRK_IMPULSETRACKER) || GetModFlag(MSF_OLDVOLSWING))
{
pChn->nRealPan = pChn->nPan + pChn->nPanSwing;
+ Limit(pChn->nRealPan, 0, 256);
} else
{
pChn->nPan += pChn->nPanSwing;
@@ -954,7 +946,6 @@
pChn->nPanSwing = 0;
pChn->nRealPan = pChn->nPan;
}
- Limit(pChn->nRealPan, 0, 256);
}
@@ -2041,7 +2032,7 @@
// vol is 14-bits
if (vol)
{
- int insVol = pChn->nInsVol; // This is the "SV" value in ITTECH.TXT
+ int insVol = pChn->nInsVol; // This is the "SV * IV" value in ITTECH.TXT
if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
ProcessVolumeSwing(pChn, insVol);
@@ -2390,14 +2381,14 @@
//ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN]);
//ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_VOLUME]);
- if(pChn->nRowCommand == CMD_MIDI || pChn->nRowCommand == CMD_SMOOTHMIDI)
+ if(pChn->rowCommand.command == CMD_MIDI || pChn->rowCommand.command == CMD_SMOOTHMIDI)
{
// Only smooth MIDI macros are processed on every tick
//if((pChn->nRowCommand == CMD_MIDI) && !(m_dwSongFlags & SONG_FIRSTTICK)) return;
- if(pChn->nRowParam < 0x80)
- ProcessMIDIMacro(nChn, (pChn->nRowCommand == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro], pChn->nRowParam);
+ if(pChn->rowCommand.param < 0x80)
+ ProcessMIDIMacro(nChn, (pChn->rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[pChn->nActiveMacro], pChn->rowCommand.param);
else
- ProcessMIDIMacro(nChn, (pChn->nRowCommand == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(pChn->nRowParam & 0x7F)], 0);
+ ProcessMIDIMacro(nChn, (pChn->rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(pChn->rowCommand.param & 0x7F)], 0);
}
}
}
@@ -2414,10 +2405,10 @@
if ((!m_nInstruments) || (m_nPattern >= Patterns.Size())
|| (m_nRow >= Patterns[m_nPattern].GetNumRows()) || (!Patterns[m_nPattern])) return;
- const MODCOMMAND::NOTE note = pChn->nRowNote;
- const MODCOMMAND::INSTR instr = pChn->nRowInstr;
- const MODCOMMAND::VOL vol = pChn->nRowVolume;
- const MODCOMMAND::VOLCMD volcmd = pChn->nRowVolCmd;
+ const MODCOMMAND::NOTE note = pChn->rowCommand.note;
+ const MODCOMMAND::INSTR instr = pChn->rowCommand.instr;
+ const MODCOMMAND::VOL vol = pChn->rowCommand.vol;
+ const MODCOMMAND::VOLCMD volcmd = pChn->rowCommand.volcmd;
// Debug
{
// Previously this function took modcommand directly from pattern. ASSERT is there
@@ -2451,15 +2442,15 @@
if (pIns && (pIns->dwFlags & INS_MUTE)) return;
}
- // Do couldn't find a valid plugin
+ // Couldn't find a valid plugin
if (pPlugin == nullptr) return;
if(GetModFlag(MSF_MIDICC_BUGEMULATION))
{
- if(note)
+ if(note != NOTE_NONE)
{
MODCOMMAND::NOTE realNote = note;
- if((note >= NOTE_MIN) && (note <= NOTE_MAX))
+ if(NOTE_IS_VALID(note))
realNote = pIns->NoteMap[note - 1];
pPlugin->MidiCommand(pIns->nMidiChannel, pIns->nMidiProgram, pIns->wMidiBank, realNote, pChn->nVolume, nChn);
} else if (volcmd == VOLCMD_VOLUME)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2011-10-02 16:40:57
|
Revision: 1076
http://modplug.svn.sourceforge.net/modplug/?rev=1076&view=rev
Author: saga-games
Date: 2011-10-02 16:40:50 +0000 (Sun, 02 Oct 2011)
Log Message:
-----------
[Fix] XM panning conversion was a bit off due to lack of rounding.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_xm.cpp
trunk/OpenMPT/soundlib/modcommand.cpp
Modified: trunk/OpenMPT/soundlib/Load_xm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_xm.cpp 2011-10-02 16:01:29 UTC (rev 1075)
+++ trunk/OpenMPT/soundlib/Load_xm.cpp 2011-10-02 16:40:50 UTC (rev 1076)
@@ -204,7 +204,7 @@
// B0-BF: Vibrato
case 0xB0: p->volcmd = VOLCMD_VIBRATODEPTH; break;
// C0-CF: Set Panning
- case 0xC0: p->volcmd = VOLCMD_PANNING; p->vol = (vol * 64 / 15); break;
+ case 0xC0: p->volcmd = VOLCMD_PANNING; p->vol = ((vol * 64 + 8) / 15); break;
// D0-DF: Panning Slide Left
case 0xD0: p->volcmd = VOLCMD_PANSLIDELEFT; break;
// E0-EF: Panning Slide Right
@@ -855,7 +855,7 @@
case VOLCMD_FINEVOLUP: vol = 0x90 + (p->vol & 0x0F); break;
case VOLCMD_VIBRATOSPEED: vol = 0xA0 + (p->vol & 0x0F); break;
case VOLCMD_VIBRATODEPTH: vol = 0xB0 + (p->vol & 0x0F); break;
- case VOLCMD_PANNING: vol = 0xC0 + (p->vol * 15 / 64); if (vol > 0xCF) vol = 0xCF; break;
+ case VOLCMD_PANNING: vol = 0xC0 + ((p->vol * 15 + 32) / 64); if (vol > 0xCF) vol = 0xCF; break;
case VOLCMD_PANSLIDELEFT: vol = 0xD0 + (p->vol & 0x0F); break;
case VOLCMD_PANSLIDERIGHT: vol = 0xE0 + (p->vol & 0x0F); break;
case VOLCMD_TONEPORTAMENTO: vol = 0xF0 + (p->vol & 0x0F); break;
Modified: trunk/OpenMPT/soundlib/modcommand.cpp
===================================================================
--- trunk/OpenMPT/soundlib/modcommand.cpp 2011-10-02 16:01:29 UTC (rev 1075)
+++ trunk/OpenMPT/soundlib/modcommand.cpp 2011-10-02 16:40:50 UTC (rev 1076)
@@ -159,7 +159,7 @@
m->volcmd = VOLCMD_VOLUME;
if(m->vol > 64) m->vol = 64;
m->command = CMD_S3MCMDEX;
- m->param = 0x80 | (m->param * 15 / 64); // XM volcol panning is 4-Bit, so we can use 4-Bit panning here.
+ m->param = 0x80 | ((m->param * 15 + 32) / 64); // XM volcol panning is 4-Bit, so we can use 4-Bit panning here.
}
break;
case CMD_PORTAMENTOUP:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2011-10-07 18:01:23
|
Revision: 1095
http://modplug.svn.sourceforge.net/modplug/?rev=1095&view=rev
Author: saga-games
Date: 2011-10-07 18:01:17 +0000 (Fri, 07 Oct 2011)
Log Message:
-----------
[Fix] A great number of mind-boggling combinations of Note Off, Instrument Number, Note Delay, etc... has been fixed, plus some things which broke in revision 1082. I might be going insane now.
Revision Links:
--------------
http://modplug.svn.sourceforge.net/modplug/?rev=1082&view=rev
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/modcommand.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-10-07 01:33:41 UTC (rev 1094)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2011-10-07 18:01:17 UTC (rev 1095)
@@ -1402,8 +1402,7 @@
if(GetNumInstruments() < 1 && instr < MAX_SAMPLES)
{
pChn->pModSample = &Samples[instr];
- }
- else
+ } else
{
if(instr < MAX_INSTRUMENTS)
pChn->pModInstrument = Instruments[instr];
@@ -1436,16 +1435,14 @@
if (instr) pChn->nNewIns = instr;
bool retrigEnv = (!note) && (instr);
+ // Apparently, any note number in a pattern causes instruments to recall their original volume settings - no matter if there's a Note Off next to it or whatever.
+ // Test cases: keyoff+instr.xm, delay.xm
+ bool reloadInstrSettings = (IsCompatibleMode(TRK_FASTTRACKER2) && instr != 0);
+ bool keepInstr = (GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT));
+
// Now it's time for some FT2 crap...
if (GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
{
- if(IsCompatibleMode(TRK_FASTTRACKER2) && instr != 0)
- {
- // Apparently, any note number in a pattern causes instruments to retrigger - no matter if there's a Note Off next to it or whatever.
- // Test cases: keyoff+instr.xm, delay.xm
- retrigEnv = true;
- }
-
// XM: FT2 ignores a note next to a K00 effect, and a fade-out seems to be done when no volume envelope is present (not exactly the Kxx behaviour)
if(cmd == CMD_KEYOFF && param == 0 && IsCompatibleMode(TRK_FASTTRACKER2))
{
@@ -1465,14 +1462,53 @@
// XM Compatibility: Some special hacks for rogue note delays... (EDx with x > 0)
// Apparently anything that is next to a note delay behaves totally unpredictable in FT2. Swedish tracker logic. :)
- // If there's a note delay but no note, retrig the last note.
if(note == NOTE_NONE)
{
+ // If there's a note delay but no real note, retrig the last note.
+ // Test case: delay2.xm
note = pChn->nNote - pChn->nTranspose;
+ retrigEnv = true;
+ } else if(note >= NOTE_MIN_SPECIAL)
+ {
+ // Gah! Even Note Off + Note Delay will cause envelopes to *retrigger*! How stupid is that?
+ retrigEnv = true;
+ // ... Well, and that is actually all it does if there's an envelope. No fade out, no nothing. *sigh*
+ // Test case: OffDelay.xm
+ note = NOTE_NONE;
+ keepInstr = false;
+ } else
+ {
+ retrigEnv = true;
+ keepInstr = true;
}
}
}
+ if(retrigEnv || reloadInstrSettings)
+ {
+ const MODSAMPLE *oldSample = nullptr;
+ // Reset default volume when retriggering envelopes
+
+ if (GetNumInstruments())
+ {
+ oldSample = pChn->pModSample;
+ } else if (instr <= GetNumSamples())
+ {
+ // Case: Only samples are used; no instruments.
+ oldSample = &Samples[instr];
+ }
+
+ if(oldSample != nullptr)
+ {
+ pChn->nVolume = oldSample->nVolume;
+ if(reloadInstrSettings)
+ {
+ // Also reload panning
+ pChn->nPan = oldSample->nPan;
+ }
+ }
+ }
+
if (retrigEnv) //Case: instrument with no note data.
{
//IT compatibility: Instrument with no note.
@@ -1490,23 +1526,16 @@
}
}
- if (m_nInstruments)
+ if (GetNumInstruments() && (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2)))
{
- if (pChn->pModSample) pChn->nVolume = pChn->pModSample->nVolume;
- if (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))
- {
- pChn->dwFlags |= CHN_FASTVOLRAMP;
- ResetChannelEnvelopes(pChn);
- pChn->nAutoVibDepth = 0;
- pChn->nAutoVibPos = 0;
- pChn->dwFlags &= ~CHN_NOTEFADE;
- pChn->nFadeOutVol = 65536;
- }
- } else //Case: Only samples are used; no instruments.
- {
- if (instr < MAX_SAMPLES) pChn->nVolume = Samples[instr].nVolume;
+ pChn->dwFlags |= CHN_FASTVOLRAMP;
+ ResetChannelEnvelopes(pChn);
+ pChn->nAutoVibDepth = 0;
+ pChn->nAutoVibPos = 0;
+ pChn->dwFlags &= ~CHN_NOTEFADE;
+ pChn->nFadeOutVol = 65536;
}
- if (!(m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT))) instr = 0;
+ if (!keepInstr) instr = 0;
}
// Invalid Instrument ?
if (instr >= MAX_INSTRUMENTS) instr = 0;
@@ -1514,15 +1543,15 @@
// Note Cut/Off/Fade => ignore instrument
if (note >= NOTE_MIN_SPECIAL) instr = 0;
- if ((note) && (note <= NOTE_MAX))
+ if (note != NOTE_NONE && NOTE_IS_VALID(note))
{
pChn->nNewNote = pChn->nLastNote = note;
- }
- // New Note Action ?
- if (note != NOTE_NONE && NOTE_IS_VALID(note) && !bPorta)
- {
- CheckNNA(nChn, instr, note, FALSE);
+ // New Note Action ?
+ if (!bPorta)
+ {
+ CheckNNA(nChn, instr, note, FALSE);
+ }
}
if(note)
@@ -1773,14 +1802,17 @@
// -> CODE#0010
// -> DESC="add extended parameter mechanism to pattern effects"
m = NULL;
- if (m_nRow < Patterns[m_nPattern].GetNumRows()-1) {
+ if (m_nRow < Patterns[m_nPattern].GetNumRows()-1)
+ {
m = Patterns[m_nPattern] + (m_nRow+1) * m_nChannels + nChn;
}
- if (m && m->command == CMD_XPARAM) {
- if (m_nType & MOD_TYPE_XM) {
+ if (m && m->command == CMD_XPARAM)
+ {
+ if (GetType() & MOD_TYPE_XM)
+ {
param -= 0x20; //with XM, 0x20 is the lowest tempo. Anything below changes ticks per row.
}
- param = (param<<8) + m->param;
+ param = (param << 8) + m->param;
}
// -! NEW_FEATURE#0010
if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))
@@ -1802,14 +1834,14 @@
case CMD_ARPEGGIO:
// IT compatibility 01. Don't ignore Arpeggio if no note is playing (also valid for ST3)
if ((m_nTickCount) || (((!pChn->nPeriod) || !pChn->nNote) && !IsCompatibleMode(TRK_IMPULSETRACKER | TRK_SCREAMTRACKER))) break;
- if ((!param) && (!(m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)))) break;
+ if ((!param) && (!(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT)))) 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 (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))
{
if (!(param & 0xF0)) param |= pChn->nRetrigParam & 0xF0;
if (!(param & 0x0F)) param |= pChn->nRetrigParam & 0x0F;
@@ -3708,7 +3740,7 @@
MODCHANNEL *pChn = &Chn[nChn];
const bool bKeyOn = (pChn->dwFlags & CHN_KEYOFF) ? false : true;
pChn->dwFlags |= CHN_KEYOFF;
- //if ((!pChn->pModInstrument) || (!(pChn->dwFlags & CHN_VOLENV)))
+ //if ((!pChn->pModInstrument) || (!(pChn->VolEnv.flags & CHN_VOLENV)))
if ((pChn->pModInstrument) && (!(pChn->VolEnv.flags & ENV_ENABLED)))
{
pChn->dwFlags |= CHN_NOTEFADE;
@@ -3739,9 +3771,10 @@
pChn->nLength = pSmp->nLength;
}
}
+
if (pChn->pModInstrument)
{
- MODINSTRUMENT *pIns = pChn->pModInstrument;
+ const MODINSTRUMENT *pIns = pChn->pModInstrument;
if (((pIns->VolEnv.dwFlags & ENV_LOOP) || (m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2))) && (pIns->nFadeOut))
{
pChn->dwFlags |= CHN_NOTEFADE;
Modified: trunk/OpenMPT/soundlib/modcommand.cpp
===================================================================
--- trunk/OpenMPT/soundlib/modcommand.cpp 2011-10-07 01:33:41 UTC (rev 1094)
+++ trunk/OpenMPT/soundlib/modcommand.cpp 2011-10-07 18:01:17 UTC (rev 1095)
@@ -597,10 +597,18 @@
m->command = m->param = 0;
}
- // Instrument numbers next to Key-Off reset instrument settings
if(m->note >= NOTE_MIN_SPECIAL)
{
+ // Instrument numbers next to Note Off reset instrument settings
m->instr = 0;
+
+ if(m->command == CMD_MODCMDEX && (m->param & 0xF0) == 0xD0)
+ {
+ // Note Off + Note Delay does nothing when using envelopes.
+ m->note = NOTE_NONE;
+ m->command = CMD_KEYOFF;
+ m->param &= 0x0F;
+ }
}
if(!m->command) switch(m->volcmd)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
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.
|
|
From: <sag...@us...> - 2011-11-18 22:21:53
|
Revision: 1136
http://modplug.svn.sourceforge.net/modplug/?rev=1136&view=rev
Author: saga-games
Date: 2011-11-18 22:21:47 +0000 (Fri, 18 Nov 2011)
Log Message:
-----------
[Fix] PSM16 Loader: Finetuning in Silverball tunes should work better now. Also fixed note transposition, it seemed to be off by one.
[Fix] PTM Loader: Fixed sample names (off-by-two error in sample struct)
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_psm.cpp
trunk/OpenMPT/soundlib/Load_ptm.cpp
Modified: trunk/OpenMPT/soundlib/Load_psm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_psm.cpp 2011-11-13 00:59:17 UTC (rev 1135)
+++ trunk/OpenMPT/soundlib/Load_psm.cpp 2011-11-18 22:21:47 UTC (rev 1136)
@@ -453,7 +453,7 @@
Samples[smp].nC5Speed = LittleEndianW(pSample->C5Freq);
Samples[smp].nLength = LittleEndian(pSample->sampleLength);
Samples[smp].nLoopStart = LittleEndian(pSample->loopStart);
- Samples[smp].nLoopEnd = LittleEndian(pSample->loopEnd);
+ Samples[smp].nLoopEnd = LittleEndian(pSample->loopEnd); // Hmm... apparently we should add +1 for Extreme Pinball tunes here? See sample 8 in the medieval table music.
Samples[smp].nPan = 128;
Samples[smp].nVolume = (pSample->defaultVolume + 1) << 1;
Samples[smp].uFlags = (pSample->flags & 0x80) ? CHN_LOOP : 0;
@@ -852,7 +852,7 @@
uint32 length; // in bytes
uint32 loopStart; // in samples?
uint32 loopEnd; // in samples?
- int8 finetune; // 0 ... 15 (useless? also, why is this almost always 70?)
+ int8 finetune; // 0 ... 15 (high nibble is 7 in most cases, but why? is it maybe some transpose value?)
uint8 volume; // default volume
uint16 c2freq;
};
@@ -943,6 +943,16 @@
Samples[iSmp].nLoopStart = LittleEndian(smphdr->loopStart);
Samples[iSmp].nLoopEnd = LittleEndian(smphdr->loopEnd);
Samples[iSmp].nC5Speed = LittleEndianW(smphdr->c2freq);
+ if(smphdr->finetune & 0x0F)
+ {
+ int finetune = smphdr->finetune & 0x0F;
+ if(finetune >= 8)
+ {
+ finetune -= 16;
+ }
+ // Copied over from DUMB
+ Samples[iSmp].nC5Speed = double(Samples[iSmp].nC5Speed) * pow(1.000225659305069791926712241547647863626, finetune * 32);
+ }
Samples[iSmp].nVolume = smphdr->volume << 2;
Samples[iSmp].nGlobalVol = 256;
@@ -1016,13 +1026,13 @@
continue;
}
- row_data = Patterns[nPat] + iRow * m_nChannels + min(bChnFlag & 0x1F, m_nChannels - 1);
+ row_data = Patterns[nPat].GetpModCommand(iRow, min(bChnFlag & 0x1F, m_nChannels - 1));
if(bChnFlag & 0x80)
{
// note + instr present
ASSERT_CAN_READ(2);
- row_data->note = lpStream[dwMemPos++] + 37;
+ row_data->note = lpStream[dwMemPos++] + 36;
row_data->instr = lpStream[dwMemPos++];
}
if(bChnFlag & 0x40)
Modified: trunk/OpenMPT/soundlib/Load_ptm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_ptm.cpp 2011-11-13 00:59:17 UTC (rev 1135)
+++ trunk/OpenMPT/soundlib/Load_ptm.cpp 2011-11-18 22:21:47 UTC (rev 1136)
@@ -38,9 +38,7 @@
WORD patseg[128]; // pattern offsets (*16)
} PTMFILEHEADER, *LPPTMFILEHEADER;
-#define SIZEOF_PTMFILEHEADER 608
-
typedef struct PTMSAMPLE
{
BYTE sampletype; // sample type (bit array)
@@ -52,13 +50,11 @@
WORD length[2]; // sample size (in bytes)
WORD loopbeg[2]; // start of loop
WORD loopend[2]; // end of loop
- WORD gusdata[8];
- char samplename[28]; // name of sample, asciiz
+ WORD gusdata[7];
+ char samplename[28]; // name of sample, asciiz
DWORD ptms_id; // sample identification, 'PTMS' or 0x534d5450
} PTMSAMPLE;
-#define SIZEOF_PTMSAMPLE 80
-
#pragma pack()
@@ -79,7 +75,7 @@
pfh.fileflags = LittleEndianW(pfh.fileflags);
pfh.reserved2 = LittleEndianW(pfh.reserved2);
pfh.ptmf_id = LittleEndian(pfh.ptmf_id);
- for (UINT j = 0; j < 128; j++)
+ for (size_t j = 0; j < CountOf(pfh.patseg); j++)
{
pfh.patseg[j] = LittleEndianW(pfh.patseg[j]);
}
@@ -89,14 +85,14 @@
|| (pfh.norders > 256) || (!pfh.norders)
|| (!pfh.nsamples) || (pfh.nsamples > 255)
|| (!pfh.npatterns) || (pfh.npatterns > 128)
- || (SIZEOF_PTMFILEHEADER+pfh.nsamples*SIZEOF_PTMSAMPLE >= (int)dwMemLength)) return false;
+ || (sizeof(PTMFILEHEADER) + pfh.nsamples * sizeof(PTMSAMPLE) >= dwMemLength)) return false;
memcpy(m_szNames[0], pfh.songname, 28);
StringFixer::SpaceToNullStringFixed<28>(m_szNames[0]);
m_nType = MOD_TYPE_PTM;
m_nChannels = pfh.nchannels;
- m_nSamples = (pfh.nsamples < MAX_SAMPLES) ? pfh.nsamples : MAX_SAMPLES-1;
- dwMemPos = SIZEOF_PTMFILEHEADER;
+ m_nSamples = min(pfh.nsamples, MAX_SAMPLES - 1);
+ dwMemPos = sizeof(PTMFILEHEADER);
nOrders = (pfh.norders < MAX_ORDERS) ? pfh.norders : MAX_ORDERS-1;
Order.ReadAsByte(pfh.orders, nOrders, nOrders);
@@ -105,7 +101,7 @@
ChnSettings[ipan].nVolume = 64;
ChnSettings[ipan].nPan = ((pfh.chnpan[ipan] & 0x0F) << 4) + 4;
}
- for (SAMPLEINDEX ismp = 0; ismp < m_nSamples; ismp++, dwMemPos += SIZEOF_PTMSAMPLE)
+ for (SAMPLEINDEX ismp = 0; ismp < m_nSamples; ismp++, dwMemPos += sizeof(PTMSAMPLE))
{
MODSAMPLE *pSmp = &Samples[ismp+1];
PTMSAMPLE *psmp = (PTMSAMPLE *)(lpStream+dwMemPos);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-01-04 00:30:56
|
Revision: 1159
http://modplug.svn.sourceforge.net/modplug/?rev=1159&view=rev
Author: saga-games
Date: 2012-01-04 00:30:49 +0000 (Wed, 04 Jan 2012)
Log Message:
-----------
[Imp] IT / MPTM Loading: Slide command parameters (Dxx etc) are now sanitized while loading, so in normal playback mode they are now treated exactly like in compatible mode.
[Mod] Playback: Changed 4-Bit panning computation a bit, so that f.e. S80 is true left and S8F is true right.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-01-03 23:44:56 UTC (rev 1158)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-01-04 00:30:49 UTC (rev 1159)
@@ -2761,7 +2761,7 @@
// IT compatibility: Ignore slide commands with both nibbles set.
if (param & 0x0F)
{
- if(!IsCompatibleMode(TRK_IMPULSETRACKER) || (param & 0xF0) == 0)
+ if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0)
newvolume -= (int)((param & 0x0F) * 4);
}
else
@@ -2807,7 +2807,7 @@
if (param & 0x0F)
{
// IT compatibility: Ignore slide commands with both nibbles set.
- if(!IsCompatibleMode(TRK_IMPULSETRACKER) || (param & 0xF0) == 0)
+ if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0)
nPanSlide = (int)((param & 0x0F) << 2);
} else
{
@@ -2894,7 +2894,7 @@
{
if (param & 0x0F)
{
- if(!IsCompatibleMode(TRK_IMPULSETRACKER) || (param & 0xF0) == 0)
+ if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0xF0) == 0)
nChnSlide = -(int)(param & 0x0F);
} else
{
@@ -2948,7 +2948,9 @@
{
if (!(m_dwSongFlags & SONG_SURROUNDPAN)) pChn->dwFlags &= ~CHN_SURROUND;
}
- pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP;
+ //pChn->nPan = (param << 4) + 8;
+ pChn->nPan = (param * 256 + 8) / 15;
+ pChn->dwFlags |= CHN_FASTVOLRAMP;
}
break;
// E9x: Retrig
@@ -3101,7 +3103,9 @@
{
if (!(m_dwSongFlags & SONG_SURROUNDPAN)) pChn->dwFlags &= ~CHN_SURROUND;
}
- pChn->nPan = (param << 4) + 8; pChn->dwFlags |= CHN_FASTVOLRAMP;
+ //pChn->nPan = (param << 4) + 8;
+ pChn->nPan = (param * 256 + 8) / 15;
+ pChn->dwFlags |= CHN_FASTVOLRAMP;
//IT compatibility 20. Set pan overrides random pan
if(IsCompatibleMode(TRK_IMPULSETRACKER))
@@ -4053,7 +4057,7 @@
if (param & 0xF0)
{
// IT compatibility: Ignore slide commands with both nibbles set.
- if(!IsCompatibleMode(TRK_IMPULSETRACKER) || (param & 0x0F) == 0)
+ if(!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (param & 0x0F) == 0)
nGlbSlide = (int)((param & 0xF0) >> 4) * 2;
} else
{
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-01-03 23:44:56 UTC (rev 1158)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-01-04 00:30:49 UTC (rev 1159)
@@ -663,26 +663,20 @@
lpStream = NULL;
}
#endif
- } else {
+ } else
+ {
// New song
m_dwCreatedWithVersion = MptVersion::num;
}
// Adjust song / sample names
- for (UINT iSmp=0; iSmp<MAX_SAMPLES; iSmp++)
+ for(SAMPLEINDEX iSmp = 0; iSmp <= GetNumSamples(); iSmp++)
{
- LPSTR p = m_szNames[iSmp];
- int j = 31;
- p[j] = 0;
- while ((j>=0) && (p[j]<=' ')) p[j--] = 0;
- while (j>=0)
- {
- if (((BYTE)p[j]) < ' ') p[j] = ' ';
- j--;
- }
+ StringFixer::SetNullTerminator(m_szNames[iSmp]);
}
+
// Adjust channels
- for (UINT ich=0; ich<MAX_BASECHANNELS; ich++)
+ for(CHANNELINDEX ich = 0; ich < MAX_BASECHANNELS; ich++)
{
if (ChnSettings[ich].nVolume > 64) ChnSettings[ich].nVolume = 64;
if (ChnSettings[ich].nPan > 256) ChnSettings[ich].nPan = 128;
@@ -700,18 +694,18 @@
}
// Checking samples
MODSAMPLE *pSmp = Samples;
- for (SAMPLEINDEX nSmp = 0; nSmp < MAX_SAMPLES; nSmp++, pSmp++)
+ for(SAMPLEINDEX nSmp = 0; nSmp < MAX_SAMPLES; nSmp++, pSmp++)
{
- if (pSmp->pSample)
+ if(pSmp->pSample)
{
- if (pSmp->nLoopEnd > pSmp->nLength) pSmp->nLoopEnd = pSmp->nLength;
- if (pSmp->nLoopStart >= pSmp->nLoopEnd)
+ if(pSmp->nLoopEnd > pSmp->nLength) pSmp->nLoopEnd = pSmp->nLength;
+ if(pSmp->nLoopStart >= pSmp->nLoopEnd)
{
pSmp->nLoopStart = 0;
pSmp->nLoopEnd = 0;
}
- if (pSmp->nSustainEnd > pSmp->nLength) pSmp->nSustainEnd = pSmp->nLength;
- if (pSmp->nSustainStart >= pSmp->nSustainEnd)
+ if(pSmp->nSustainEnd > pSmp->nLength) pSmp->nSustainEnd = pSmp->nLength;
+ if(pSmp->nSustainStart >= pSmp->nSustainEnd)
{
pSmp->nSustainStart = 0;
pSmp->nSustainEnd = 0;
@@ -724,9 +718,9 @@
pSmp->nSustainStart = 0;
pSmp->nSustainEnd = 0;
}
- if (!pSmp->nLoopEnd) pSmp->uFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);
- if (!pSmp->nSustainEnd) pSmp->uFlags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
- if (pSmp->nGlobalVol > 64) pSmp->nGlobalVol = 64;
+ if(!pSmp->nLoopEnd) pSmp->uFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);
+ if(!pSmp->nSustainEnd) pSmp->uFlags &= ~(CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
+ if(pSmp->nGlobalVol > 64) pSmp->nGlobalVol = 64;
}
// Check invalid instruments
while ((m_nInstruments > 0) && (!Instruments[m_nInstruments])) m_nInstruments--;
@@ -2996,6 +2990,31 @@
}
}
+ if((pSndFile->GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
+ {
+ // In the IT format, slide commands with both nibbles set should be ignored.
+ // For note volume slides, OpenMPT 1.18 fixes this in compatible mode, OpenMPT 1.20 fixes this in normal mode as well.
+ const bool noteVolSlide =
+ (pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00) ||
+ (!pSndFile->IsCompatibleMode(TRK_IMPULSETRACKER) && pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00)))
+ &&
+ (m.command == CMD_VOLUMESLIDE || m.command == CMD_VIBRATOVOL || m.command == CMD_TONEPORTAVOL || m.command == CMD_PANNINGSLIDE);
+
+ // OpenMPT 1.20 also fixes this for global volume and channel volume slides.
+ const bool chanVolSlide =
+ (pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
+ &&
+ (m.command == CMD_GLOBALVOLSLIDE || m.command == CMD_CHANNELVOLSLIDE);
+
+ if(noteVolSlide || chanVolSlide)
+ {
+ if((m.param & 0x0F) != 0x00 && (m.param & 0x0F) != 0x0F && (m.param & 0xF0) != 0x00 && (m.param & 0xF0) != 0xF0)
+ {
+ m.param &= 0x0F;
+ }
+ }
+ }
+
if(pSndFile->GetType() == MOD_TYPE_XM)
{
if(pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00) ||
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-03-17 19:06:13
|
Revision: 1221
http://modplug.svn.sourceforge.net/modplug/?rev=1221&view=rev
Author: saga-games
Date: 2012-03-17 19:06:05 +0000 (Sat, 17 Mar 2012)
Log Message:
-----------
[Fix] Approx real BPM was broken for classic tempo mode since a while (http://bugs.openmpt.org/view.php?id=237)
[Fix] Pattern delays are now handled correctly in IT format, and almost correctly in the S3M format. Files made with old versions of OpenMPT are upgraded automatically so they are not affected by the behaviour change.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/Sndmix.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-03-13 22:15:28 UTC (rev 1220)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-03-17 19:06:05 UTC (rev 1221)
@@ -163,7 +163,7 @@
for (;;)
{
- UINT nSpeedCount = 0;
+ UINT rowDelay = 0, tickDelay = 0;
nRow = nNextRow;
nCurrentOrder = nNextOrder;
@@ -356,15 +356,42 @@
memory.musicTempo = CLAMP(memory.musicTempo, GetModSpecifications().tempoMin, GetModSpecifications().tempoMax);
// -! NEW_FEATURE#0010
break;
- // Pattern Delay
+
case CMD_S3MCMDEX:
- if ((param & 0xF0) == 0x60) { nSpeedCount = param & 0x0F; break; } else
- if ((param & 0xF0) == 0xA0) { pChn->nOldHiOffset = param & 0x0F; break; } else
- if ((param & 0xF0) == 0xB0) { param &= 0x0F; param |= 0x60; }
+ if((param & 0xF0) == 0x60)
+ {
+ // Fine Pattern Delay
+ tickDelay += (param & 0x0F);
+ } else if((param & 0xF0) == 0xE0 && !rowDelay)
+ {
+ // Pattern Delay
+ rowDelay = (param & 0x0F);
+ } else if((param & 0xF0) == 0xA0)
+ {
+ // High sample offset
+ pChn->nOldHiOffset = param & 0x0F;
+ } else if((param & 0xF0) == 0xB0)
+ {
+ // Pattern Loop
+ if (param & 0x0F)
+ {
+ memory.elapsedTime += (memory.elapsedTime - memory.patLoop[nChn]) * (double)(param & 0x0F);
+ } else
+ {
+ memory.patLoop[nChn] = memory.elapsedTime;
+ memory.patLoopStart[nChn] = nRow;
+ }
+ }
+ break;
+
case CMD_MODCMDEX:
- if ((param & 0xF0) == 0xE0) nSpeedCount = (param & 0x0F) * memory.musicSpeed; else
- if ((param & 0xF0) == 0x60)
+ if((param & 0xF0) == 0xE0)
{
+ // Pattern Delay
+ rowDelay = (param & 0x0F);
+ } else if ((param & 0xF0) == 0x60)
+ {
+ // Pattern Loop
if (param & 0x0F)
{
memory.elapsedTime += (memory.elapsedTime - memory.patLoop[nChn]) * (double)(param & 0x0F);
@@ -376,6 +403,7 @@
}
}
break;
+
case CMD_XFINEPORTAUPDOWN:
// ignore high offset in compatible mode
if (((param & 0xF0) == 0xA0) && !IsCompatibleMode(TRK_FASTTRACKER2)) pChn->nOldHiOffset = param & 0x0F;
@@ -428,11 +456,9 @@
{
//IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
if (param) memory.oldGlbVolSlide[nChn] = param; else param = memory.oldGlbVolSlide[nChn];
- }
- else
+ } else
{
if (param) memory.oldGlbVolSlide[0] = param; else param = memory.oldGlbVolSlide[0];
-
}
if (((param & 0x0F) == 0x0F) && (param & 0xF0))
{
@@ -495,7 +521,7 @@
}
// XXX this does not take per-pattern time signatures into consideration!
- memory.elapsedTime += GetRowDuration(memory.musicTempo, memory.musicSpeed, nSpeedCount);
+ memory.elapsedTime += GetRowDuration(memory.musicTempo, memory.musicSpeed, (memory.musicSpeed + tickDelay) * (1 + rowDelay));
}
if(retval.targetReached || endOrder == ORDERINDEX_INVALID || endRow == ROWINDEX_INVALID)
@@ -926,7 +952,8 @@
if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED))
{
note += pChn->nTranspose;
- Limit(note, NOTE_MIN + 11, NOTE_MIN + 130); // why 131? 120+11, how does this make sense?
+ // RealNote = PatternNote + RelativeTone; (0..118, 0 = C-0, 118 = A#9)
+ Limit(note, NOTE_MIN + 11, NOTE_MIN + 130); // 119 possible notes
} else
{
Limit(note, NOTE_MIN, NOTE_MAX);
@@ -1415,12 +1442,13 @@
//-------------------------------
{
ModChannel *pChn = Chn;
- ROWINDEX nBreakRow = ROWINDEX_INVALID, nPatLoopRow = ROWINDEX_INVALID;
+ ROWINDEX nBreakRow = ROWINDEX_INVALID; // Is changed if a break to row command is encountere.d
+ ROWINDEX nPatLoopRow = ROWINDEX_INVALID; // Is changed if a pattern loop jump-back is executed
ORDERINDEX nPosJump = ORDERINDEX_INVALID;
// -> CODE#0010
// -> DESC="add extended parameter mechanism to pattern effects"
- ModCommand* m = nullptr;
+ ModCommand *m = nullptr;
// -! NEW_FEATURE#0010
for (CHANNELINDEX nChn = 0; nChn < m_nChannels; nChn++, pChn++)
{
@@ -1502,8 +1530,7 @@
nStartTick = (param & 0xF0) >> 4;
const UINT cutAtTick = nStartTick + (param & 0x0F);
NoteCut(nChn, cutAtTick);
- } else
- if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX))
+ } else if ((cmd == CMD_MODCMDEX) || (cmd == CMD_S3MCMDEX))
{
if ((!param) && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT))) param = pChn->nOldCmdEx; else pChn->nOldCmdEx = param;
// Note Delay ?
@@ -1536,8 +1563,7 @@
}
continue;
}
- } else
- if(m_dwSongFlags & SONG_FIRSTTICK)
+ } else if(m_dwSongFlags & SONG_FIRSTTICK)
{
// Pattern Loop ?
if ((((param & 0xF0) == 0x60) && (cmd == CMD_MODCMDEX))
@@ -1566,11 +1592,16 @@
Chn[i].nPatternLoopCount = pChn->nPatternLoopCount;
}
}
- } else
- // Pattern Delay
- if ((param & 0xF0) == 0xE0)
+ } else if ((param & 0xF0) == 0xE0)
{
- m_nPatternDelay = param & 0x0F;
+ // Pattern Delay
+ // In Scream Tracker 3 / Impulse Tracker, only the first delay command on this row is considered.
+ // Test cases: PatternDelays.it, PatternDelays.s3m, PatternDelays.xm
+ // XXX In Scream Tracker 3, the "left" channels are evaluated before the "right" channels, which is not emulated here!
+ if(!(GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)) || !m_nPatternDelay)
+ {
+ m_nPatternDelay = param & 0x0F;
+ }
}
}
}
@@ -1657,7 +1688,7 @@
const ModSample *oldSample = nullptr;
// Reset default volume when retriggering envelopes
- if (GetNumInstruments())
+ if(GetNumInstruments())
{
oldSample = pChn->pModSample;
} else if (instr <= GetNumSamples())
@@ -1682,13 +1713,14 @@
//IT compatibility: Instrument with no note.
if(IsCompatibleMode(TRK_IMPULSETRACKER) || (m_dwSongFlags & SONG_PT1XMODE))
{
- if(m_nInstruments)
+ if(GetNumInstruments())
{
+ // Instrument mode
if(instr < MAX_INSTRUMENTS && pChn->pModInstrument != Instruments[instr])
note = pChn->nNote;
- }
- else //Case: Only samples used
+ } else
{
+ // Sample mode
if(instr < MAX_SAMPLES && pChn->pSample != Samples[instr].pSample)
note = pChn->nNote;
}
@@ -1809,7 +1841,7 @@
so... hxx = (hx | (oldhxx & 0xf0)) ???
TODO is this done correctly?
*/
- if ((volcmd > VOLCMD_PANNING) && (m_nTickCount >= nStartTick))
+ if ((volcmd > VOLCMD_PANNING) && (m_nTickCount >= nStartTick))
{
if (volcmd == VOLCMD_TONEPORTAMENTO)
{
@@ -1977,7 +2009,7 @@
{
if ((GetType() & MOD_TYPE_XM))
{
- param -= 0x20; //with XM, 0x20 is the lowest tempo. Anything below changes ticks per row.
+ param -= 0x20; //with XM, 0x20 is the lowest tempo. Anything below changes ticks per row.
}
param = (param << 8) + m->param;
}
@@ -2324,11 +2356,15 @@
const bool doPatternLoop = (nPatLoopRow != ROWINDEX_INVALID);
// Pattern Loop
- if (doPatternLoop)
+ if(doPatternLoop)
{
m_nNextOrder = m_nCurrentOrder;
m_nNextRow = nPatLoopRow;
- if (m_nPatternDelay) m_nNextRow++;
+ if(m_nPatternDelay)
+ {
+ m_nNextRow++;
+ }
+
// As long as the pattern loop is running, mark the looped rows as not visited yet
for(ROWINDEX nRow = nPatLoopRow; nRow <= m_nRow; nRow++)
{
@@ -2338,23 +2374,23 @@
// Pattern Break / Position Jump only if no loop running
// Test case for FT2 exception: PatLoop-Jumps.xm, PatLoop-Various.xm
- if ((nBreakRow != ROWINDEX_INVALID || nPosJump != ORDERINDEX_INVALID)
+ if((nBreakRow != ROWINDEX_INVALID || nPosJump != ORDERINDEX_INVALID)
&& (!doPatternLoop || IsCompatibleMode(TRK_FASTTRACKER2)))
{
- if (nPosJump == ORDERINDEX_INVALID) nPosJump = m_nCurrentOrder + 1;
- if (nBreakRow == ROWINDEX_INVALID) nBreakRow = 0;
+ if(nPosJump == ORDERINDEX_INVALID) nPosJump = m_nCurrentOrder + 1;
+ if(nBreakRow == ROWINDEX_INVALID) nBreakRow = 0;
m_dwSongFlags |= SONG_BREAKTOROW;
- if (nPosJump >= Order.size())
+ if(nPosJump >= Order.size())
{
nPosJump = 0;
}
// IT / FT2 compatibility: don't reset loop count on pattern break.
// Test case: gm-trippy01.it, PatLoop-Break.xm, PatLoop-Weird.xm
- if (nPosJump != m_nCurrentOrder && !IsCompatibleMode(TRK_IMPULSETRACKER | TRK_FASTTRACKER2))
+ if(nPosJump != m_nCurrentOrder && !IsCompatibleMode(TRK_IMPULSETRACKER | TRK_FASTTRACKER2))
{
- for (CHANNELINDEX i = 0; i < GetNumChannels(); i++)
+ for(CHANNELINDEX i = 0; i < GetNumChannels(); i++)
{
Chn[i].nPatternLoopCount = 0;
}
@@ -3068,15 +3104,17 @@
break;
// S6x: Pattern Delay for x frames
case 0x60:
- if(IsCompatibleMode(TRK_IMPULSETRACKER))
+ if((m_dwSongFlags & SONG_FIRSTTICK) && m_nTickCount == 0)
{
- if(!(m_dwSongFlags & SONG_FIRSTTICK) || m_nTickCount > 0) break;
+ // Tick delays are added up.
+ // Scream Tracker 3 does actually not support this command.
+ // We'll use the same behaviour as for Impulse Tracker, as we can assume that
+ // most S3Ms that make use of this command were made with Impulse Tracker.
+ // MPT added this command to the XM format through the X6x effect, so we will use
+ // the same behaviour here as well.
+ // Test cases: PatternDelays.it, PatternDelays.s3m, PatternDelays.xm
m_nFrameDelay += param;
}
- else
- {
- m_nFrameDelay = param;
- }
break;
// S7x: Envelope Control / Instrument Control
case 0x70: if(!(m_dwSongFlags & SONG_FIRSTTICK)) break;
@@ -4101,7 +4139,7 @@
}
if (nGlbSlide)
{
- if (!(m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT))) nGlbSlide *= 2;
+ if (!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) nGlbSlide *= 2;
nGlbSlide += m_nGlobalVolume;
Limit(nGlbSlide, 0, 256);
m_nGlobalVolume = nGlbSlide;
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-03-13 22:15:28 UTC (rev 1220)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-03-17 19:06:05 UTC (rev 1221)
@@ -2658,14 +2658,16 @@
{
switch(m_nTempoMode)
{
- case tempo_mode_classic: default:
+ case tempo_mode_classic:
+ default:
m_nSamplesPerTick = (gdwMixingFreq * 5 * m_nTempoFactor) / (m_nMusicTempo << 8);
+ break;
- case tempo_mode_modern:
+ case tempo_mode_modern:
m_nSamplesPerTick = gdwMixingFreq * (60 / m_nMusicTempo / (m_nMusicSpeed * m_nCurrentRowsPerBeat));
break;
- case tempo_mode_alternative:
+ case tempo_mode_alternative:
m_nSamplesPerTick = gdwMixingFreq / m_nMusicTempo;
break;
}
@@ -2673,25 +2675,29 @@
// Get the duration of a row in milliseconds, based on the current rows per beat and given speed and tempo settings.
-// "additionalTicks" are ticks that are derived from Row Delay effects.
-double CSoundFile::GetRowDuration(UINT tempo, UINT speed, UINT additionalTicks) const
-//-----------------------------------------------------------------------------------
+// "speedIncludingPatternDelays" is the total row length, including the ticks from Row Delay effects.
+// It is required because modern tempo mode normally doesn't consider "speed", so "speedIncludingPatternDelays" is
+// used as a ratio.
+double CSoundFile::GetRowDuration(UINT tempo, UINT speed, UINT speedIncludingPatternDelays) const
+//-----------------------------------------------------------------------------------------------
{
+ speedIncludingPatternDelays = Util::Max(speedIncludingPatternDelays, speed);
+
switch(m_nTempoMode)
{
case tempo_mode_classic:
default:
- return static_cast<double>(2500 * (speed + additionalTicks)) / static_cast<double>(tempo);
+ return static_cast<double>(2500 * speedIncludingPatternDelays) / static_cast<double>(tempo);
case tempo_mode_modern:
{
// If there are any row delay effects, the row length factor compensates for those.
- const double rowLength = static_cast<double>(speed + additionalTicks) / static_cast<double>(speed);
+ const double rowLength = static_cast<double>(speedIncludingPatternDelays) / static_cast<double>(speed);
return 60000.0 * rowLength / static_cast<double>(tempo) / static_cast<double>(m_nCurrentRowsPerBeat);
}
case tempo_mode_alternative:
- return static_cast<double>(1000 * (speed + additionalTicks)) / static_cast<double>(tempo);
+ return static_cast<double>(1000 * speedIncludingPatternDelays) / static_cast<double>(tempo);
}
}
@@ -2855,6 +2861,7 @@
UpgradePatternData(CSoundFile *pSndFile)
{
this->pSndFile = pSndFile;
+ chn = 0;
}
void operator()(ModCommand& m)
@@ -2909,6 +2916,46 @@
}
}
+ if(pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 20, 00, 00))
+ {
+ // Pattern Delay fixes
+
+ const bool fixS6x = (m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0x60);
+ // We also fix X6x commands in hacked XM files, since they are treated identically to the S6x command in IT/S3M files.
+ // We don't treat them in files made with OpenMPT 1.18+ that have compatible play enabled, though, since they are ignored there anyway.
+ const bool fixX6x = (m.command == CMD_XFINEPORTAUPDOWN && (m.param & 0xF0) == 0x60
+ && (!pSndFile->IsCompatibleMode(TRK_FASTTRACKER2) || pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 18, 00, 00)));
+
+ if(fixS6x || fixX6x)
+ {
+ // OpenMPT 1.20 fixes multiple fine pattern delays on the same row. Previously, only the last command was considered,
+ // but all commands should be added up. Since Scream Tracker 3 itself doesn't support S6x, we also use Impulse Tracker's behaviour here,
+ // since we can assume that most S3Ms that make use of S6x were composed with Impulse Tracker.
+ ModCommand *fixCmd = (&m) - chn;
+ for(CHANNELINDEX i = 0; i < chn; i++, fixCmd++)
+ {
+ if(fixCmd->command == CMD_S3MCMDEX && (fixCmd->param & 0xF0) == 0x60)
+ {
+ fixCmd->command = CMD_NONE;
+ }
+ }
+ }
+
+ if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xE0)
+ {
+ // OpenMPT 1.20 fixes multiple pattern delays on the same row. Previously, only the *last* command was considered,
+ // but Scream Tracker 3 and Impulse Tracker only consider the *first* command.
+ ModCommand *fixCmd = (&m) - chn;
+ for(CHANNELINDEX i = 0; i < chn; i++, fixCmd++)
+ {
+ if(fixCmd->command == CMD_S3MCMDEX && (fixCmd->param & 0xF0) == 0xE0)
+ {
+ fixCmd->command = CMD_NONE;
+ }
+ }
+ }
+ }
+
if(pSndFile->GetType() == MOD_TYPE_XM)
{
if(pSndFile->m_dwLastSavedWithVersion < MAKE_VERSION_NUMERIC(1, 19, 00, 00) ||
@@ -2923,9 +2970,16 @@
}
}
+ chn++;
+ if(chn >= pSndFile->GetNumChannels())
+ {
+ chn = 0;
+ }
+
}
CSoundFile *pSndFile;
+ CHANNELINDEX chn;
};
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-03-13 22:15:28 UTC (rev 1220)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-03-17 19:06:05 UTC (rev 1221)
@@ -348,7 +348,7 @@
DWORD GetSongTime() { return static_cast<DWORD>(GetLength(eNoAdjust).duration + 0.5); }
void RecalculateSamplesPerTick();
- double GetRowDuration(UINT tempo, UINT speed, UINT additionalTicks = 0) const;
+ double GetRowDuration(UINT tempo, UINT speed, UINT speedIncludingPatternDelays = 0) const;
// A repeat count value of -1 means infinite loop
void SetRepeatCount(int n) { m_nRepeatCount = n; }
@@ -574,7 +574,11 @@
DWORD IsSongFinished(UINT nOrder, UINT nRow) const;
void UpdateTimeSignature();
- UINT GetNumTicksOnCurrentRow() const { return m_nMusicSpeed * (m_nPatternDelay + 1) + m_nFrameDelay; };
+ UINT GetNumTicksOnCurrentRow() const
+ {
+ return (m_nMusicSpeed + m_nFrameDelay) * (m_nPatternDelay + 1);
+ }
+
public:
// Write pattern effect functions
bool TryWriteEffect(PATTERNINDEX nPat, ROWINDEX nRow, BYTE nEffect, BYTE nParam, bool bIsVolumeEffect, CHANNELINDEX nChn = CHANNELINDEX_INVALID, bool bAllowMultipleEffects = true, writeEffectAllowRowChange allowRowChange = weIgnore, bool bRetry = true);
Modified: trunk/OpenMPT/soundlib/Sndmix.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndmix.cpp 2012-03-13 22:15:28 UTC (rev 1220)
+++ trunk/OpenMPT/soundlib/Sndmix.cpp 2012-03-17 19:06:05 UTC (rev 1221)
@@ -897,9 +897,14 @@
if (m_nTickCount)
{
m_dwSongFlags &= ~SONG_FIRSTTICK;
- if ((!(m_nType & MOD_TYPE_XM)) && (m_nTickCount < GetNumTicksOnCurrentRow()))
+ if(!(GetType() & MOD_TYPE_XM) && m_nTickCount < GetNumTicksOnCurrentRow())
{
- if (!(m_nTickCount % m_nMusicSpeed)) m_dwSongFlags |= SONG_FIRSTTICK;
+ // Emulate first tick behaviour if Row Delay is set.
+ // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm
+ if(!(m_nTickCount % (m_nMusicSpeed + m_nFrameDelay)))
+ {
+ m_dwSongFlags |= SONG_FIRSTTICK;
+ }
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-03-20 16:27:25
|
Revision: 1225
http://modplug.svn.sourceforge.net/modplug/?rev=1225&view=rev
Author: saga-games
Date: 2012-03-20 16:27:14 +0000 (Tue, 20 Mar 2012)
Log Message:
-----------
[Fix] FT2 note limit was not always applied properly (test case: note-limit 2.xm)
[Fix] FT2 envelope loop escape check was not applied properly.
[Mod] Removed zero-volume envelope loop optimisation (it can break stuff in some of my rewritten code and I can't see a point in it).
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndmix.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-03-18 18:02:50 UTC (rev 1224)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-03-20 16:27:14 UTC (rev 1225)
@@ -721,7 +721,10 @@
{
pChn->nInsVol = pSmp->nGlobalVol;
}
- if (pSmp->uFlags & CHN_PANNING) pChn->nPan = pSmp->nPan;
+ if((pSmp->uFlags & CHN_PANNING) || (GetType() & MOD_TYPE_XM))
+ {
+ pChn->nPan = pSmp->nPan;
+ }
}
@@ -1622,11 +1625,35 @@
UINT note = pChn->rowCommand.note;
if (instr) pChn->nNewIns = instr;
- // Notes that exceed FT2's limit are completely ignored.
- // Test case: note-limit.xm
if(ModCommand::IsNote(note) && IsCompatibleMode(TRK_FASTTRACKER2))
{
- const int computedNote = note + pChn->nTranspose;
+ // Notes that exceed FT2's limit are completely ignored.
+ // Test case: note-limit.xm
+ int transpose = pChn->nTranspose;
+ if(instr && !bPorta)
+ {
+ // Refresh transpose
+ // Test case: note-limit 2.xm
+ SAMPLEINDEX sample = SAMPLEINDEX_INVALID;
+ if(GetNumInstruments())
+ {
+ // Instrument mode
+ if(instr <= GetNumInstruments() && Instruments[instr] != nullptr)
+ {
+ sample = Instruments[instr]->Keyboard[note];
+ }
+ } else
+ {
+ // Sample mode
+ sample = instr;
+ }
+ if(sample <= GetNumSamples())
+ {
+ transpose = GetSample(instr).RelativeTone;
+ }
+ }
+
+ const int computedNote = note + transpose;
if((computedNote < NOTE_MIN + 11 || computedNote > NOTE_MIN + 130))
{
note = NOTE_NONE;
Modified: trunk/OpenMPT/soundlib/Sndmix.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndmix.cpp 2012-03-18 18:02:50 UTC (rev 1224)
+++ trunk/OpenMPT/soundlib/Sndmix.cpp 2012-03-20 16:27:14 UTC (rev 1225)
@@ -1238,7 +1238,7 @@
// FT2 compatibility: If the sustain point is at the loop end and the sustain loop has been released, don't loop anymore.
// Test case: EnvLoops.xm
- const bool escapeLoop = (insEnv.nLoopEnd == insEnv.nSustainEnd && (pChn->dwFlags & CHN_KEYOFF) && IsCompatibleMode(TRK_FASTTRACKER2));
+ const bool escapeLoop = (insEnv.nLoopEnd == insEnv.nSustainEnd && (insEnv.dwFlags & ENV_SUSTAIN) && (pChn->dwFlags & CHN_KEYOFF) && IsCompatibleMode(TRK_FASTTRACKER2));
if(position == end && !escapeLoop)
{
@@ -1248,8 +1248,9 @@
&& (!(GetType() & MOD_TYPE_XM) || (insEnv.nLoopEnd + 1u == insEnv.nNodes)))
{
// Stop channel if the envelope loop only covers the last silent envelope point.
- pChn->dwFlags |= CHN_NOTEFADE;
- pChn->nFadeOutVol = 0;
+ // Can't see a point in this, and it breaks doommix3.xm if you allow loading one-point loops, so disabling it for now.
+ //pChn->dwFlags |= CHN_NOTEFADE;
+ //pChn->nFadeOutVol = 0;
}
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-03-30 20:13:06
|
Revision: 1232
http://modplug.svn.sourceforge.net/modplug/?rev=1232&view=rev
Author: saga-games
Date: 2012-03-30 20:12:57 +0000 (Fri, 30 Mar 2012)
Log Message:
-----------
[Ref] Introduced a FileReader class which is safer and more intuitive to use that the good old memory pointer + length stuff. Should be used from now on. A few mod loaders have already been converted, more to follow.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Endianness.h
trunk/OpenMPT/soundlib/ITTools.cpp
trunk/OpenMPT/soundlib/Load_669.cpp
trunk/OpenMPT/soundlib/Load_gdm.cpp
trunk/OpenMPT/soundlib/Load_okt.cpp
trunk/OpenMPT/soundlib/Loaders.h
trunk/OpenMPT/soundlib/ModSequence.cpp
trunk/OpenMPT/soundlib/ModSequence.h
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/XMTools.cpp
trunk/OpenMPT/soundlib/load_j2b.cpp
Modified: trunk/OpenMPT/soundlib/Endianness.h
===================================================================
--- trunk/OpenMPT/soundlib/Endianness.h 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Endianness.h 2012-03-30 20:12:57 UTC (rev 1232)
@@ -19,6 +19,7 @@
// endian architecture or value x in format of current architecture to little endian
// format.
+// Deprecated. Use "SwapBytesXX" versions below.
#ifdef PLATFORM_BIG_ENDIAN
// PPC
inline DWORD LittleEndian(DWORD x) { return ((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24); }
@@ -32,3 +33,26 @@
#define LittleEndian(x) (x)
#define LittleEndianW(x) (x)
#endif
+
+#ifdef PLATFORM_BIG_ENDIAN
+// PPC
+inline void SwapBytesBE(uint32 &value) { value; }
+inline void SwapBytesBE(uint16 &value) { value; }
+inline void SwapBytesLE(uint32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesLE(uint16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+inline void SwapBytesBE(int32 &value) { value; }
+inline void SwapBytesBE(int16 &value) { value; }
+inline void SwapBytesLE(int32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesLE(int16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+#else
+// x86
+inline void SwapBytesBE(uint32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesBE(uint16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+inline void SwapBytesLE(uint32 &value) { value; }
+inline void SwapBytesLE(uint16 &value) { value; }
+inline void SwapBytesBE(int32 &value) { value = ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) | ((value & 0xFF000000) >> 24); }
+inline void SwapBytesBE(int16 &value) { value = (((value >> 8) & 0xFF) | ((value << 8) & 0xFF00)); }
+inline void SwapBytesLE(int32 &value) { value; }
+inline void SwapBytesLE(int16 &value) { value; }
+#endif
+
Modified: trunk/OpenMPT/soundlib/ITTools.cpp
===================================================================
--- trunk/OpenMPT/soundlib/ITTools.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/ITTools.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -18,18 +18,18 @@
void ITFileHeader::ConvertEndianness()
//------------------------------------
{
- id = LittleEndian(id);
- ordnum = LittleEndianW(ordnum);
- insnum = LittleEndianW(insnum);
- smpnum = LittleEndianW(smpnum);
- patnum = LittleEndianW(patnum);
- cwtv = LittleEndianW(cwtv);
- cmwt = LittleEndianW(cmwt);
- flags = LittleEndianW(flags);
- special = LittleEndianW(special);
- msglength = LittleEndianW(msglength);
- msgoffset = LittleEndian(msgoffset);
- reserved = LittleEndian(reserved);
+ SwapBytesLE(id);
+ SwapBytesLE(ordnum);
+ SwapBytesLE(insnum);
+ SwapBytesLE(smpnum);
+ SwapBytesLE(patnum);
+ SwapBytesLE(cwtv);
+ SwapBytesLE(cmwt);
+ SwapBytesLE(flags);
+ SwapBytesLE(special);
+ SwapBytesLE(msglength);
+ SwapBytesLE(msgoffset);
+ SwapBytesLE(reserved);
}
@@ -617,9 +617,9 @@
fattime = static_cast<uint16>((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11));
runtime = static_cast<uint32>(mptHistory.openTime * (18.2f / HISTORY_TIMER_PRECISION));
- fatdate = LittleEndianW(fatdate);
- fattime = LittleEndianW(fattime);
- runtime = LittleEndian(runtime);
+ SwapBytesLE(fatdate);
+ SwapBytesLE(fattime);
+ SwapBytesLE(runtime);
}
#endif // MODPLUG_TRACKER
Modified: trunk/OpenMPT/soundlib/Load_669.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -13,52 +13,59 @@
#include "stdafx.h"
#include "Loaders.h"
-#pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data"
+#pragma pack(push, 1)
-typedef struct tagFILEHEADER669
+struct _669FileHeader
{
- WORD sig; // 'if' or 'JN'
- signed char songmessage[108]; // Song Message
- BYTE samples; // number of samples (1-64)
- BYTE patterns; // number of patterns (1-128)
- BYTE restartpos;
- BYTE orders[128];
- BYTE tempolist[128];
- BYTE breaks[128];
-} FILEHEADER669;
+ uint16 sig; // 'if' or 'JN'
+ char songmessage[108]; // Song Message
+ uint8 samples; // number of samples (1-64)
+ uint8 patterns; // number of patterns (1-128)
+ uint8 restartpos;
+ uint8 orders[128];
+ uint8 tempolist[128];
+ uint8 breaks[128];
+};
+STATIC_ASSERT(sizeof(_669FileHeader) == 497);
-typedef struct tagSAMPLE669
+struct _669Sample
{
- char filename[13];
+ char filename[13];
uint32 length; // when will somebody think about DWORD align ???
uint32 loopStart;
uint32 loopEnd;
-} SAMPLE669;
+};
+STATIC_ASSERT(sizeof(_669Sample) == 25);
-bool CSoundFile::Read669(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+#pragma pack(pop)
+
+
+bool CSoundFile::Read669(FileReader &file)
+//----------------------------------------
{
- BOOL b669Ext;
- const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream;
- const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1);
- DWORD dwMemPos = 0;
+ bool has669Ext;
+ _669FileHeader fileHeader;
+
+ file.Rewind();
+ if(!file.Read(fileHeader))
+ {
+ return false;
+ }
- if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return false;
- if ((LittleEndianW(pfh->sig) != 0x6669) && (LittleEndianW(pfh->sig) != 0x4E4A)) return false;
- b669Ext = (LittleEndianW(pfh->sig) == 0x4E4A) ? TRUE : FALSE;
- if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128)
- || (!pfh->patterns) || (pfh->patterns > 128)) return false;
- DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600;
- if (dontfuckwithme > dwMemLength) return false;
- for (UINT ichk=0; ichk<pfh->samples; ichk++)
+ if(fileHeader.sig != LittleEndianW(0x6669) && fileHeader.sig != LittleEndianW(0x4E4A))
{
- DWORD len = LittleEndian(psmp[ichk].length);
- dontfuckwithme += len;
+ return false;
}
- if (dontfuckwithme - 0x1F1 > dwMemLength) return false;
- // That should be enough checking: this must be a 669 module.
+
+ has669Ext = fileHeader.sig == LittleEndianW(0x4E4A);
+ if(fileHeader.samples > 64 || fileHeader.restartpos >= 128
+ || fileHeader.patterns > 128)
+ {
+ return false;
+ }
+
m_nType = MOD_TYPE_669;
m_dwSongFlags |= SONG_LINEARSLIDES;
m_nMinPeriod = 28 << 2;
@@ -68,14 +75,20 @@
m_nChannels = 8;
// Copy first song message line into song title
- StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], reinterpret_cast<const char *>(pfh->songmessage), 36);
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], fileHeader.songmessage, 36);
- m_nSamples = pfh->samples;
- for (SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++, psmp++)
+ m_nSamples = fileHeader.samples;
+ for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++)
{
- DWORD len = LittleEndian(psmp->length);
- DWORD loopstart = LittleEndian(psmp->loopStart);
- DWORD loopend = LittleEndian(psmp->loopEnd);
+ _669Sample sample;
+ if(!file.Read(sample))
+ {
+ return false;
+ }
+
+ DWORD len = LittleEndian(sample.length);
+ DWORD loopstart = LittleEndian(sample.loopStart);
+ DWORD loopend = LittleEndian(sample.loopEnd);
if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
if ((loopend > len) && (!loopstart)) loopend = 0;
if (loopend > len) loopend = len;
@@ -84,19 +97,19 @@
Samples[nSmp].nLoopStart = loopstart;
Samples[nSmp].nLoopEnd = loopend;
if (loopend) Samples[nSmp].uFlags |= CHN_LOOP;
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSmp], psmp->filename);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSmp], sample.filename);
Samples[nSmp].nVolume = 256;
Samples[nSmp].nGlobalVol = 64;
Samples[nSmp].nPan = 128;
}
// Song Message
- ReadFixedLineLengthMessage((BYTE *)(&pfh->songmessage), 108, 36, 0);
+ ReadFixedLineLengthMessage(file, 108, 36, 0);
// Reading Orders
- Order.ReadAsByte(pfh->orders, 128, 128);
- m_nRestartPos = pfh->restartpos;
- if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0;
+ Order.ReadAsByte(fileHeader.orders, 128, 128);
+ m_nRestartPos = fileHeader.restartpos;
+ if(Order[m_nRestartPos] >= fileHeader.patterns) m_nRestartPos = 0;
// Reading Pattern Break Locations
for (UINT npan=0; npan<8; npan++)
{
@@ -105,18 +118,16 @@
}
// Reading Patterns
- dwMemPos = 0x1F1 + pfh->samples * 25;
- for (UINT npat=0; npat<pfh->patterns; npat++)
+ for (UINT npat = 0; npat < fileHeader.patterns; npat++)
{
if(Patterns.Insert(npat, 64))
break;
ModCommand *m = Patterns[npat];
- const BYTE *p = lpStream + dwMemPos;
for (UINT row=0; row<64; row++)
{
ModCommand *mspeed = m;
- if ((row == pfh->breaks[npat]) && (row != 63))
+ if ((row == fileHeader.breaks[npat]) && (row != 63))
{
for (UINT i=0; i<8; i++)
{
@@ -124,25 +135,31 @@
m[i].param = 0;
}
}
- for (UINT n=0; n<8; n++, m++, p+=3)
+ for(UINT n = 0; n < 8; n++, m++)
{
- UINT note = p[0] >> 2;
- UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
- UINT vol = p[1] & 0x0F;
- if (p[0] < 0xFE)
+ char data[3];
+ if(!file.ReadArray(data))
{
+ break;
+ }
+
+ UINT note = data[0] >> 2;
+ UINT instr = ((data[0] & 0x03) << 4) | (data[1] >> 4);
+ UINT vol = data[1] & 0x0F;
+ if (data[0] < 0xFE)
+ {
m->note = note + 37;
m->instr = instr + 1;
}
- if (p[0] <= 0xFE)
+ if (data[0] <= 0xFE)
{
m->volcmd = VOLCMD_VOLUME;
m->vol = (vol << 2) + 2;
}
- if (p[2] != 0xFF)
+ if (data[2] != 0xFF)
{
- UINT command = p[2] >> 4;
- UINT param = p[2] & 0x0F;
+ UINT command = data[2] >> 4;
+ UINT param = data[2] & 0x0F;
switch(command)
{
case 0x00: command = CMD_PORTAMENTOUP; break;
@@ -170,23 +187,17 @@
for (UINT i=0; i<8; i++) if (!mspeed[i].command)
{
mspeed[i].command = CMD_SPEED;
- mspeed[i].param = pfh->tempolist[npat] + 2;
+ mspeed[i].param = fileHeader.tempolist[npat] + 2;
break;
}
}
}
- dwMemPos += 0x600;
}
// Reading Samples
- for (UINT n=1; n<=m_nSamples; n++)
+ for(SAMPLEINDEX n = 1; n <= m_nSamples; n++)
{
- UINT len = Samples[n].nLength;
- if (dwMemPos >= dwMemLength) break;
- if (len > 4) ReadSample(&Samples[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
- dwMemPos += len;
+ ReadSample(&Samples[n], RS_PCM8U, file);
}
return true;
}
-
-
Modified: trunk/OpenMPT/soundlib/Load_gdm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_gdm.cpp 2012-03-27 21:11:51 UTC (rev 1231)
+++ trunk/OpenMPT/soundlib/Load_gdm.cpp 2012-03-30 20:12:57 UTC (rev 1232)
@@ -15,494 +15,501 @@
#include "stdafx.h"
#include "Loaders.h"
-#ifdef MODPLUG_TRACKER
-#include "../mptrack/moddoc.h"
-#endif // MODPLUG_TRACKER
#pragma pack(push, 1)
-struct GDMHEADER
+// GDM File Header
+struct GDMFileHeader
{
- uint32 ID; // ID: 'GDM\xFE'
- char SongTitle[32]; // Music's title
- char SongMusician[32]; // Name of music's composer
- char DOSEOF[3]; // 13, 10, 26
- uint32 ID2; // ID: 'GMFS'
- uint8 FormMajorVer; // Format major version
- uint8 FormMinorVer; // Format minor version
- uint16 TrackID; // Composing Tracker ID code (00 = 2GDM)
- uint8 TrackMajorVer; // Tracker's major version
- uint8 TrackMinorVer; // Tracker's minor version
- uint8 PanMap[32]; // 0-Left to 15-Right, 255-N/U
- uint8 MastVol; // Range: 0...64
- uint8 Tempo; // Initial music tempo (6)
- uint8 BPM; // Initial music BPM (125)
- uint16 FormOrigin; // Original format ID:
+ // Header magic bytes
+ enum HeaderMagic
+ {
+ magicGDM_ = 0xFE4D4447,
+ magicGMFS = 0x53464D47,
+ };
+
+ uint32 magic; // ID: 'GDM\xFE'
+ char songTitle[32]; // Music's title
+ char songMusician[32]; // Name of music's composer
+ char dosEOF[3]; // 13, 10, 26
+ uint32 magic2; // ID: 'GMFS'
+ uint8 formatMajorVer; // Format major version
+ uint8 formatMinorVer; // Format minor version
+ uint16 trackerID; // Composing Tracker ID code (00 = 2GDM)
+ uint8 trackerMajorVer; // Tracker's major version
+ uint8 trackerMinorVer; // Tracker's minor version
+ uint8 panMap[32]; // 0-Left to 15-Right, 255-N/U
+ uint8 masterVol; // Range: 0...64
+ uint8 tempo; // Initial music tempo (6)
+ uint8 bpm; // Initial music BPM (125)
+ uint16 originalFormat; // Original format ID:
// 1-MOD, 2-MTM, 3-S3M, 4-669, 5-FAR, 6-ULT, 7-STM, 8-MED
// (versions of 2GDM prior to v1.15 won't set this correctly)
- uint32 OrdOffset;
- uint8 NOO; // Number of orders in module - 1
- uint32 PatOffset;
- uint8 NOP; // Number of patterns in module - 1
- uint32 SamHeadOffset;
- uint32 SamOffset;
- uint8 NOS; // Number of samples in module - 1
- uint32 MTOffset; // Offset of song message
- uint32 MTLength;
- uint32 SSOffset; // Offset of scrolly script (huh?)
- uint16 SSLength;
- uint32 TGOffset; // Offset of text graphic (huh?)
- uint16 TGLength;
+ uint32 orderOffset;
+ uint8 lastOrder; // Number of orders in module - 1
+ uint32 patternOffset;
+ uint8 lastPattern; // Number of patterns in module - 1
+ uint32 sampleHeaderOffset;
+ uint32 sampleDataOffset;
+ uint8 lastSample; // Number of samples in module - 1
+ uint32 messageTextOffset; // Offset of song message
+ uint32 messageTextLength;
+ uint32 scrollyScriptOffset; // Offset of scrolly script (huh?)
+ uint16 scrollyScriptLength;
+ uint32 textGraphicOffset; // Offset of text graphic (huh?)
+ uint16 textGraphicLength;
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(magic);
+ SwapBytesLE(magic2);
+ SwapBytesLE(trackerID);
+ SwapBytesLE(originalFormat);
+ SwapBytesLE(orderOffset);
+ SwapBytesLE(patternOffset);
+ SwapBytesLE(sampleHeaderOffset);
+ SwapBytesLE(sampleDataOffset);
+ SwapBytesLE(messageTextOffset);
+ SwapBytesLE(messageTextLength);
+ SwapBytesLE(messageTextOffset);
+ SwapBytesLE(messageTextLength);
+ SwapBytesLE(scrollyScriptOffset);
+ SwapBytesLE(scrollyScriptLength);
+ SwapBytesLE(textGraphicOffset);
+ SwapBytesLE(textGraphicLength);
+ }
};
-struct GDMSAMPLEHEADER
+// GDM Sample Header
+struct GDMSampleHeader
{
- char SamName[32]; // sample's name
- char FileName[12]; // sample's filename
- uint8 EmsHandle; // useless
- uint32 Length; // length in bytes
- uint32 LoopBegin; // loop start in samples
- uint32 LoopEnd; // loop end in samples
- uint8 Flags; // misc. flags
- uint16 C4Hertz; // frequency
- uint8 Volume; // default volume
- uint8 Pan; // default pan
+ enum SampleFlags
+ {
+ smpLoop = 0x01,
+ smp16Bit = 0x02, // 16-Bit samples are not handled correctly by 2GDM (not implemented)
+ smpVolume = 0x04,
+ smpPanning = 0x08,
+ smpLZW = 0x10, // LZW-compressed samples are not implemented in 2GDM
+ smpStereo = 0x20, // Stereo samples are not handled correctly by 2GDM (not implemented)
+ };
+
+ char name[32]; // sample's name
+ char fileName[12]; // sample's filename
+ uint8 emsHandle; // useless
+ uint32 length; // length in bytes
+ uint32 loopBegin; // loop start in samples
+ uint32 loopEnd; // loop end in samples
+ uint8 flags; // misc. flags
+ uint16 c4Hertz; // frequency
+ uint8 volume; // default volume
+ uint8 panning; // default pan
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopBegin);
+ SwapBytesLE(loopEnd);
+ SwapBytesLE(c4Hertz);
+ }
};
#pragma pack(pop)
-#define GDMHEAD_GDM_ 0xFE4D4447
-#define GDMHEAD_GMFS 0x53464D47
-
-static const MODTYPE GDMHeader_Origin[] =
+bool CSoundFile::ReadGDM(FileReader &file)
+//----------------------------------------
{
- MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED
-};
+ file.Rewind();
+ GDMFileHeader fileHeader;
+ if(!file.ReadConvertEndianness(fileHeader))
+ {
+ return false;
+ }
-bool CSoundFile::ReadGDM(const LPCBYTE lpStream, const DWORD dwMemLength)
-//-----------------------------------------------------------------------
-{
- if ((!lpStream) || (dwMemLength < sizeof(GDMHEADER))) return false;
-
- const GDMHEADER *pHeader = (GDMHEADER *)lpStream;
-
// Is it a valid GDM file?
- if( (pHeader->ID != LittleEndian(GDMHEAD_GDM_)) || //GDM\xFE
- (pHeader->DOSEOF[0] != 13 || pHeader->DOSEOF[1] != 10 || pHeader->DOSEOF[2] != 26) || //CR+LF+EOF
- (pHeader->ID2 != LittleEndian(GDMHEAD_GMFS))) return false; //GMFS
-
- // There are no other format versions...
- if(pHeader->FormMajorVer != 1 || pHeader->FormMinorVer != 0)
+ if(fileHeader.magic != GDMFileHeader::magicGDM_
+ || fileHeader.dosEOF[0] != 13 || fileHeader.dosEOF[1] != 10 || fileHeader.dosEOF[2] != 26
+ || fileHeader.magic2 != GDMFileHeader::magicGMFS
+ || fileHeader.formatMajorVer != 1 || fileHeader.formatMinorVer != 0)
+ {
return false;
+ }
// 1-MOD, 2-MTM, 3-S3M, 4-669, 5-FAR, 6-ULT, 7-STM, 8-MED
- m_nType = GDMHeader_Origin[pHeader->FormOrigin % CountOf(GDMHeader_Origin)];
+ static const MODTYPE gdmFormatOrigin[] =
+ {
+ MOD_TYPE_NONE, MOD_TYPE_MOD, MOD_TYPE_MTM, MOD_TYPE_S3M, MOD_TYPE_669, MOD_TYPE_FAR, MOD_TYPE_ULT, MOD_TYPE_STM, MOD_TYPE_MED
+ };
+
+ m_nType = gdmFormatOrigin[fileHeader.originalFormat % CountOf(gdmFormatOrigin)];
if(m_nType == MOD_TYPE_NONE)
+ {
return false;
+ }
- // Interesting question: Is TrackID, TrackMajorVer, TrackMinorVer relevant? The only TrackID should be 0 - 2GDM.exe, so the answer would be no.
-
// Song name
MemsetZero(m_szNames);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], pHeader->SongTitle);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.songTitle);
// Read channel pan map... 0...15 = channel panning, 16 = surround channel, 255 = channel does not exist
m_nChannels = 32;
for(CHANNELINDEX i = 0; i < 32; i++)
{
- if(pHeader->PanMap[i] < 16)
+ if(fileHeader.panMap[i] < 16)
{
- ChnSettings[i].nPan = min((pHeader->PanMap[i] * 16) + 8, 256);
+ ChnSettings[i].nPan = min((fileHeader.panMap[i] * 16) + 8, 256);
}
- else if(pHeader->PanMap[i] == 16)
+ else if(fileHeader.panMap[i] == 16)
{
ChnSettings[i].nPan = 128;
ChnSettings[i].dwFlags |= CHN_SURROUND;
}
- else if(pHeader->PanMap[i] == 0xFF)
+ else if(fileHeader.panMap[i] == 0xFF)
{
m_nChannels = i;
break;
}
}
- m_nDefaultGlobalVolume = min(pHeader->MastVol * 4, 256);
- m_nDefaultSpeed = pHeader->Tempo;
- m_nDefaultTempo = pHeader->BPM;
+ m_nDefaultGlobalVolume = min(fileHeader.masterVol * 4, 256);
+ m_nDefaultSpeed = fileHeader.tempo;
+ m_nDefaultTempo = fileHeader.bpm;
m_nRestartPos = 0; // Not supported in this format, so use the default value
m_nSamplePreAmp = 48; // Dito
m_nVSTiVolume = 48; // Dito
- uint32 iSampleOffset = LittleEndian(pHeader->SamOffset),
- iPatternsOffset = LittleEndian(pHeader->PatOffset);
+ // Read orders
+ if(file.Seek(fileHeader.orderOffset))
+ {
+ Order.ReadAsByte(file, fileHeader.lastOrder + 1);
+ }
- const uint32 iOrdOffset = LittleEndian(pHeader->OrdOffset), iSamHeadOffset = LittleEndian(pHeader->SamHeadOffset),
- iMTOffset = LittleEndian(pHeader->MTOffset), iMTLength = LittleEndian(pHeader->MTLength),
- iSSOffset = LittleEndian(pHeader->SSOffset), iSSLength = LittleEndianW(pHeader->SSLength),
- iTGOffset = LittleEndian(pHeader->TGOffset), iTGLength = LittleEndianW(pHeader->TGLength);
-
-
- // Check if offsets are valid. we won't read the scrolly text or text graphics, but invalid pointers would probably indicate a broken file...
- if( dwMemLength < iOrdOffset || dwMemLength - iOrdOffset < pHeader->NOO
- || dwMemLength < iPatternsOffset
- || dwMemLength < iSamHeadOffset || dwMemLength - iSamHeadOffset < (pHeader->NOS + 1) * sizeof(GDMSAMPLEHEADER)
- || dwMemLength < iSampleOffset
- || dwMemLength < iMTOffset || dwMemLength - iMTOffset < iMTLength
- || dwMemLength < iSSOffset || dwMemLength - iSSOffset < iSSLength
- || dwMemLength < iTGOffset || dwMemLength - iTGOffset < iTGLength)
+ // Read samples
+ if(!file.Seek(fileHeader.sampleHeaderOffset))
+ {
return false;
+ }
- // Read orders
- Order.ReadAsByte(lpStream + iOrdOffset, pHeader->NOO + 1, dwMemLength - iOrdOffset);
+ m_nSamples = fileHeader.lastSample + 1;
- // Read samples
- m_nSamples = pHeader->NOS + 1;
-
- for(SAMPLEINDEX iSmp = 1; iSmp <= m_nSamples; iSmp++)
+ // Sample headers
+ for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
{
- const GDMSAMPLEHEADER *pSample = (GDMSAMPLEHEADER *)(lpStream + iSamHeadOffset + (iSmp - 1) * sizeof(GDMSAMPLEHEADER));
+ GDMSampleHeader gdmSample;
+ if(!file.ReadConvertEndianness(gdmSample))
+ {
+ break;
+ }
- // Sample header
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], gdmSample.name);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(Samples[smp].filename, gdmSample.fileName);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[iSmp], pSample->SamName);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(Samples[iSmp].filename, pSample->FileName);
+ Samples[smp].nC5Speed = gdmSample.c4Hertz;
+ Samples[smp].nGlobalVol = 256; // Not supported in this format
- Samples[iSmp].nC5Speed = LittleEndianW(pSample->C4Hertz);
- Samples[iSmp].nGlobalVol = 256; // not supported in this format
- Samples[iSmp].nLength = min(LittleEndian(pSample->Length), MAX_SAMPLE_LENGTH); // in bytes
- Samples[iSmp].nLoopStart = min(LittleEndian(pSample->LoopBegin), Samples[iSmp].nLength); // in samples
- Samples[iSmp].nLoopEnd = min(LittleEndian(pSample->LoopEnd) - 1, Samples[iSmp].nLength); // dito
- FrequencyToTranspose(&Samples[iSmp]); // set transpose + finetune for mod files
+ Samples[smp].nLength = min(gdmSample.length, MAX_SAMPLE_LENGTH); // in bytes
+ // Sample format
+ if(gdmSample.flags & GDMSampleHeader::smp16Bit)
+ {
+ Samples[smp].uFlags |= CHN_16BIT;
+ Samples[smp].nLength /= 2;
+ }
+
+ Samples[smp].nLoopStart = min(gdmSample.loopBegin, Samples[smp].nLength); // in samples
+ Samples[smp].nLoopEnd = min(gdmSample.loopEnd - 1, Samples[smp].nLength); // dito
+ FrequencyToTranspose(&Samples[smp]); // set transpose + finetune for mod files
+
// Fix transpose + finetune for some rare cases where transpose is not C-5 (e.g. sample 4 in wander2.gdm)
if(m_nType == MOD_TYPE_MOD)
{
- while(Samples[iSmp].RelativeTone != 0)
+ while(Samples[smp].RelativeTone != 0)
{
- if(Samples[iSmp].RelativeTone > 0)
+ if(Samples[smp].RelativeTone > 0)
{
- Samples[iSmp].RelativeTone -= 1;
- Samples[iSmp].nFineTune += 128;
+ Samples[smp].RelativeTone -= 1;
+ Samples[smp].nFineTune += 128;
}
else
{
- Samples[iSmp].RelativeTone += 1;
- Samples[iSmp].nFineTune -= 128;
+ Samples[smp].RelativeTone += 1;
+ Samples[smp].nFineTune -= 128;
}
}
}
- if(pSample->Flags & 0x01) Samples[iSmp].uFlags |= CHN_LOOP; // Loop sample
+ Samples[smp].uFlags = 0;
+ if(gdmSample.flags & GDMSampleHeader::smpLoop) Samples[smp].uFlags |= CHN_LOOP; // Loop sample
- if(pSample->Flags & 0x04)
+ if(gdmSample.flags & GDMSampleHeader::smpVolume)
{
- Samples[iSmp].nVolume = min(pSample->Volume, 64) * 4; // 0...64, 255 = no default volume
- }
- else
+ // Default volume is used... 0...64, 255 = no default volume
+ Samples[smp].nVolume = min(gdmSample.volume, 64) * 4;
+ } else
{
- Samples[iSmp].nVolume = 256; // Default volume
+ Samples[smp].nVolume = 256;
}
- if(pSample->Flags & 0x08) // Default panning is used
+ if(gdmSample.flags & GDMSampleHeader::smpPanning)
{
- Samples[iSmp].uFlags |= CHN_PANNING;
- Samples[iSmp].nPan = (pSample->Pan > 15) ? 128 : min((pSample->Pan * 16) + 8, 256); // 0...15, 16 = surround (not supported), 255 = no default panning
- }
- else
+ // Default panning is used
+ Samples[smp].uFlags |= CHN_PANNING;
+ // 0...15, 16 = surround (not supported), 255 = no default panning
+ Samples[smp].nPan = (gdmSample.panning > 15) ? 128 : min((gdmSample.panning * 16) + 8, 256);
+ } else
{
- Samples[iSmp].nPan = 128;
+ Samples[smp].nPan = 128;
}
+ }
- // Note: apparently (and according to zilym), 2GDM doesn't handle 16 bit or stereo samples properly.
- // so those flags are pretty much meaningless and we will ignore them... in fact, samples won't load as expected if we don't!
-
- UINT iSampleFormat;
- if(pSample->Flags & 0x02) // 16 bit
+ // Read sample data
+ if(file.Seek(fileHeader.sampleDataOffset))
+ {
+ for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
{
- if(pSample->Flags & 0x20) // stereo
- iSampleFormat = RS_PCM16U; // should be RS_STPCM16U but that breaks the sample reader
- else
- iSampleFormat = RS_PCM16U;
+ ReadSample(&Samples[smp], (Samples[smp].uFlags & CHN_16BIT) ? RS_PCM16U : RS_PCM8U, file);
}
- else // 8 bit
- {
- if(pSample->Flags & 0x20) // stereo
- iSampleFormat = RS_PCM8U; // should be RS_STPCM8U - dito
- else
- iSampleFormat = RS_PCM8U;
- }
-
- // According to zilym, LZW support has never been finished, so this is also practically useless. Just ignore the flag.
- // if(pSample->Flags & 0x10) {...}
-
- // Read sample data
- ReadSample(&Samples[iSmp], iSampleFormat, reinterpret_cast<LPCSTR>(lpStream + iSampleOffset), dwMemLength - iSampleOffset);
-
- iSampleOffset += min(LittleEndian(pSample->Length), dwMemLength - iSampleOffset);
-
}
// Read patterns
- Patterns.ResizeArray(max(MAX_PATTERNS, pHeader->NOP + 1));
+ Patterns.ResizeArray(max(MAX_PATTERNS, fileHeader.lastPattern + 1));
- const bool bS3MCommandSet = (GetBestSaveFormat() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)) != 0;
+ const CModSpecifications &modSpecs = GetModSpecifications(GetBestSaveFormat());
- // We'll start at position iPatternsOffset and decode all patterns
- for(PATTERNINDEX iPat = 0; iPat < pHeader->NOP + 1; iPat++)
+ // We'll start at position patternsOffset and decode all patterns
+ file.Seek(fileHeader.patternOffset);
+ for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++)
{
-
- if(iPatternsOffset + 2 > dwMemLength) break;
- uint16 iPatternLength = LittleEndianW(*(uint16 *)(lpStream + iPatternsOffset)); // Pattern length including the two "length" bytes
- if(iPatternLength > dwMemLength || iPatternsOffset > dwMemLength - iPatternLength) break;
-
- if(Patterns.Insert(iPat, 64))
+ if(!file.CanRead(2))
+ {
break;
+ }
- // Position in THIS pattern
- DWORD iPatternPos = iPatternsOffset + 2;
+ // Read pattern length *including* the two "length" bytes
+ uint16 patternLength = file.ReadUint16LE();
- for(ROWINDEX iRow = 0; iRow < 64; iRow++)
+ if(patternLength <= 2)
{
- ModCommand *p = Patterns[iPat].GetRow(iRow);
+ // Huh, no pattern data present?
+ continue;
+ }
+ FileReader chunk = file.GetChunk(patternLength - 2);
- while(true) // Zero byte = next row
- {
- if(iPatternPos + 1 > dwMemLength) break;
+ if(!chunk.IsValid() || Patterns.Insert(pat, 64))
+ {
+ break;
+ }
- BYTE bChannel = lpStream[iPatternPos++];
+ enum
+ {
+ rowDone = 0, // Advance to next row
+ channelMask = 0x1F, // Mask for retrieving channel information
+ noteFlag = 0x20, // Note / instrument information present
+ effectFlag = 0x40, // Effect information present
+ effectMask = 0x1F, // Mask for retrieving effect command
+ effectDone = 0x20, // Last effect in this channel
+ };
- if(bChannel == 0) break; // Next row, please!
+ for(ROWINDEX row = 0; row < 64; row++)
+ {
+ PatternRow rowBase = Patterns[pat].GetRow(row);
- const UINT channel = bChannel & 0x1F;
+ uint8 channelByte;
+ // If channel byte is zero, advance to next row.
+ while((channelByte = chunk.ReadUint8()) != rowDone)
+ {
+ CHANNELINDEX channel = channelByte & channelMask;
if(channel >= m_nChannels) break; // Better safe than sorry!
- ModCommand *m = &p[channel];
+ ModCommand &m = rowBase[channel];
- if(bChannel & 0x20)
+ if(channelByte & noteFlag)
{
// Note and sample follows
- if(iPatternPos + 2 > dwMemLength) break;
- BYTE bNote = lpStream[iPatternPos++];
- BYTE bSample = lpStream[iPatternPos++];
+ uint8 noteByte = chunk.ReadUint8();
+ uint8 noteSample = chunk.ReadUint8();
- bNote = (bNote & 0x7F) - 1; // This format doesn't have note cuts
- if(bNote < 0xF0) bNote = (bNote & 0x0F) + 12 * (bNote >> 4) + 13;
- if(bNote == 0xFF) bNote = NOTE_NONE;
- m->note = bNote;
- m->instr = bSample;
-
+ if(noteByte)
+ {
+ noteByte = (noteByte & 0x7F) - 1; // This format doesn't have note cuts
+ if(noteByte < 0xF0) note...
[truncated message content] |
|
From: <sag...@us...> - 2012-03-31 17:46:45
|
Revision: 1234
http://modplug.svn.sourceforge.net/modplug/?rev=1234&view=rev
Author: saga-games
Date: 2012-03-31 17:46:38 +0000 (Sat, 31 Mar 2012)
Log Message:
-----------
[Imp] Rewrote UMX loader. Instead of "scanning" the file, it actually parses Unreal files now. As a nice side effect, it can now also load samples from UAX files.
[Fix] 669 Loader broke some revisions ago.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_669.cpp
trunk/OpenMPT/soundlib/Load_umx.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Load_669.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-30 23:29:11 UTC (rev 1233)
+++ trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-31 17:46:38 UTC (rev 1234)
@@ -17,6 +17,12 @@
struct _669FileHeader
{
+ enum MagicBytes
+ {
+ magic669 = 0x6669, // 'if'
+ magic669Ext = 0x4E4A // 'JN'
+ };
+
uint16 sig; // 'if' or 'JN'
char songmessage[108]; // Song Message
uint8 samples; // number of samples (1-64)
@@ -25,6 +31,12 @@
uint8 orders[128];
uint8 tempolist[128];
uint8 breaks[128];
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(sig);
+ }
};
STATIC_ASSERT(sizeof(_669FileHeader) == 497);
@@ -32,9 +44,17 @@
struct _669Sample
{
char filename[13];
- uint32 length; // when will somebody think about DWORD align ???
+ uint32 length;
uint32 loopStart;
uint32 loopEnd;
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopStart);
+ SwapBytesLE(loopEnd);
+ }
};
STATIC_ASSERT(sizeof(_669Sample) == 25);
@@ -45,21 +65,20 @@
bool CSoundFile::Read669(FileReader &file)
//----------------------------------------
{
- bool has669Ext;
_669FileHeader fileHeader;
file.Rewind();
- if(!file.Read(fileHeader))
+ if(!file.ReadConvertEndianness(fileHeader))
{
return false;
}
- if(fileHeader.sig != LittleEndianW(0x6669) && fileHeader.sig != LittleEndianW(0x4E4A))
+ if(fileHeader.sig != _669FileHeader::magic669 && fileHeader.sig != _669FileHeader::magic669Ext)
{
return false;
}
- has669Ext = fileHeader.sig == LittleEndianW(0x4E4A);
+ //bool has669Ext = fileHeader.sig == _669FileHeader::magic669Ext;
if(fileHeader.samples > 64 || fileHeader.restartpos >= 128
|| fileHeader.patterns > 128)
{
@@ -74,81 +93,76 @@
m_nDefaultSpeed = 6;
m_nChannels = 8;
- // Copy first song message line into song title
- StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], fileHeader.songmessage, 36);
-
m_nSamples = fileHeader.samples;
- for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++)
+ for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
{
_669Sample sample;
- if(!file.Read(sample))
+ if(!file.ReadConvertEndianness(sample))
{
return false;
}
- DWORD len = LittleEndian(sample.length);
- DWORD loopstart = LittleEndian(sample.loopStart);
- DWORD loopend = LittleEndian(sample.loopEnd);
- if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
- if ((loopend > len) && (!loopstart)) loopend = 0;
- if (loopend > len) loopend = len;
- if (loopstart + 4 >= loopend) loopstart = loopend = 0;
- Samples[nSmp].nLength = len;
- Samples[nSmp].nLoopStart = loopstart;
- Samples[nSmp].nLoopEnd = loopend;
- if (loopend) Samples[nSmp].uFlags |= CHN_LOOP;
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nSmp], sample.filename);
- Samples[nSmp].nVolume = 256;
- Samples[nSmp].nGlobalVol = 64;
- Samples[nSmp].nPan = 128;
+ SmpLength len = sample.length;
+ SmpLength loopstart = sample.loopStart;
+ SmpLength loopend = sample.loopEnd;
+ if(len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH;
+ if((loopend > len) && (!loopstart)) loopend = 0;
+ if(loopend > len) loopend = len;
+ if(loopstart + 4 >= loopend) loopstart = loopend = 0;
+ Samples[smp].nLength = len;
+ Samples[smp].nLoopStart = loopstart;
+ Samples[smp].nLoopEnd = loopend;
+ if(loopend) Samples[smp].uFlags |= CHN_LOOP;
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sample.filename);
+ Samples[smp].nVolume = 256;
+ Samples[smp].nGlobalVol = 64;
+ Samples[smp].nPan = 128;
}
+ // Copy first song message line into song title
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], fileHeader.songmessage, 36);
// Song Message
- ReadFixedLineLengthMessage(file, 108, 36, 0);
+ ReadFixedLineLengthMessage(reinterpret_cast<const BYTE *>(fileHeader.songmessage), 108, 36, 0);
// Reading Orders
Order.ReadAsByte(fileHeader.orders, 128, 128);
m_nRestartPos = fileHeader.restartpos;
if(Order[m_nRestartPos] >= fileHeader.patterns) m_nRestartPos = 0;
- // Reading Pattern Break Locations
- for (UINT npan=0; npan<8; npan++)
+
+ // Set up panning
+ for(CHANNELINDEX chn = 0; chn < 8; chn++)
{
- ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0;
- ChnSettings[npan].nVolume = 64;
+ ChnSettings[chn].nPan = (chn & 1) ? 0x30 : 0xD0;
+ ChnSettings[chn].nVolume = 64;
}
// Reading Patterns
- for (UINT npat = 0; npat < fileHeader.patterns; npat++)
+ for(PATTERNINDEX pat = 0; pat < fileHeader.patterns; pat++)
{
- if(Patterns.Insert(npat, 64))
- break;
+ if(Patterns.Insert(pat, 64))
+ {
+ continue;
+ }
- ModCommand *m = Patterns[npat];
- for (UINT row=0; row<64; row++)
+ ModCommand *m = Patterns[pat];
+ for(ROWINDEX row = 0; row < 64; row++)
{
ModCommand *mspeed = m;
- if ((row == fileHeader.breaks[npat]) && (row != 63))
+
+ for(CHANNELINDEX n = 0; n < 8; n++, m++)
{
- for (UINT i=0; i<8; i++)
- {
- m[i].command = CMD_PATTERNBREAK;
- m[i].param = 0;
- }
- }
- for(UINT n = 0; n < 8; n++, m++)
- {
- char data[3];
+ uint8 data[3];
if(!file.ReadArray(data))
{
break;
}
- UINT note = data[0] >> 2;
- UINT instr = ((data[0] & 0x03) << 4) | (data[1] >> 4);
- UINT vol = data[1] & 0x0F;
+ uint8 note = data[0] >> 2;
+ uint8 instr = ((data[0] & 0x03) << 4) | (data[1] >> 4);
+ uint8 vol = data[1] & 0x0F;
if (data[0] < 0xFE)
{
- m->note = note + 37;
+ m->note = note + 36 + NOTE_MIN;
m->instr = instr + 1;
}
if (data[0] <= 0xFE)
@@ -158,8 +172,8 @@
}
if (data[2] != 0xFF)
{
- UINT command = data[2] >> 4;
- UINT param = data[2] & 0x0F;
+ uint8 command = data[2] >> 4;
+ uint8 param = data[2] & 0x0F;
switch(command)
{
case 0x00: command = CMD_PORTAMENTOUP; break;
@@ -187,10 +201,16 @@
for (UINT i=0; i<8; i++) if (!mspeed[i].command)
{
mspeed[i].command = CMD_SPEED;
- mspeed[i].param = fileHeader.tempolist[npat] + 2;
+ mspeed[i].param = fileHeader.tempolist[pat] + 2;
break;
}
}
+
+ // Write pattern break
+ if(fileHeader.breaks[pat] < 63)
+ {
+ TryWriteEffect(pat, fileHeader.breaks[pat], CMD_PATTERNBREAK, 0, false, CHANNELINDEX_INVALID, false, weTryPreviousRow);
+ }
}
}
Modified: trunk/OpenMPT/soundlib/Load_umx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_umx.cpp 2012-03-30 23:29:11 UTC (rev 1233)
+++ trunk/OpenMPT/soundlib/Load_umx.cpp 2012-03-31 17:46:38 UTC (rev 1234)
@@ -1,10 +1,10 @@
/*
* Load_umx.cpp
* ------------
- * Purpose: UMX (Unreal Music) module ripper
+ * Purpose: UMX (Unreal Music) and UAX (Unreal Sounds) module ripper
* Notes : Obviously, this code only rips modules from older Unreal Engine games, such as Unreal 1, Unreal Tournament 1 and Deus Ex.
- * Authors: Olivier Lapicque
- * OpenMPT Devs
+ * For UAX sound packages, the sounds are read into module sample slots.
+ * Authors: Johannes Schultz (inspired by code from http://wiki.beyondunreal.com/Legacy:Package_File_Format)
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
@@ -12,47 +12,297 @@
#include "stdafx.h"
#include "Loaders.h"
-#define MODMAGIC_OFFSET (20 + 31 * 30 + 130)
+#pragma pack(push, 1)
+// UMX File Header
+struct UMXFileHeader
+{
+ // Magic Bytes
+ enum UMXMagic
+ {
+ magicBytes = 0x9E2A83C1,
+ };
-bool CSoundFile::ReadUMX(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+ uint32 magic;
+ uint16 packageVersion;
+ uint16 licenseMode;
+ uint32 flags;
+ uint32 nameCount;
+ uint32 nameOffset;
+ uint32 exportCount;
+ uint32 exportOffset;
+ uint32 importCount;
+ uint32 importOffset;
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(magic);
+ SwapBytesLE(packageVersion);
+ // Don't need the rest.
+ SwapBytesLE(nameCount);
+ SwapBytesLE(nameOffset);
+ SwapBytesLE(exportCount);
+ SwapBytesLE(exportOffset);
+ SwapBytesLE(importCount);
+ SwapBytesLE(importOffset);
+ }
+};
+
+#pragma pack(pop)
+
+
+// Read compressed unreal integers - similar to MIDI integers, but signed values are possible.
+int32 ReadUMXIndex(FileReader &chunk)
+//-----------------------------------
{
- if ((!lpStream) || (dwMemLength < 0x800)) return false;
- // Rip Mods from UMX
- if ((LittleEndian(*((DWORD *)(lpStream+0x20))) < dwMemLength)
- && (LittleEndian(*((DWORD *)(lpStream+0x18))) <= dwMemLength - 0x10)
- && (LittleEndian(*((DWORD *)(lpStream+0x18))) >= dwMemLength - 0x200))
+ enum
{
- for (UINT uscan=0x40; uscan<0x500; uscan++)
+ signMask = 0x80, // Highest bit of first byte indicates if value is signed
+ valueMask1 = 0x3F, // Low 6 bits of first byte are actual value
+ continueMask1 = 0x40, // Second-highest bit of first byte indicates if further bytes follow
+ valueMask = 0x7F, // Low 7 bits of following bytes are actual value
+ continueMask = 0x80, // Highest bit of following bytes indicates if further bytes follow
+ };
+
+ // Read first byte
+ uint8 b = chunk.ReadUint8();
+ bool isSigned = (b & signMask) != 0;
+ int32 result = (b & valueMask1);
+ int shift = 6;
+
+ if(b & continueMask1)
+ {
+ // Read remaining bytes
+ do
{
- DWORD dwScan = LittleEndian(*((DWORD *)(lpStream+uscan)));
- // IT
- if (dwScan == 0x4D504D49)
+ b = chunk.ReadUint8();
+ int32 data = static_cast<int32>(b) & valueMask;
+ data <<= shift;
+ result |= data;
+ shift += 7;
+ } while((b & continueMask) != 0 && (shift < 32));
+ }
+
+ if(isSigned)
+ {
+ result = -result;
+ }
+ return result;
+}
+
+
+// Read an entry from the name table.
+std::string ReadUMXNameTableEntry(FileReader &chunk, uint16 packageVersion)
+//-------------------------------------------------------------------------
+{
+ std::string name;
+ if(packageVersion >= 64)
+ {
+ // String length
+ int32 length = ReadUMXIndex(chunk);
+ if(length <= 0)
+ {
+ return "";
+ }
+ name.reserve(length);
+ }
+
+ // Simple zero-terminated string
+ uint8 chr;
+ while((chr = chunk.ReadUint8()) != 0)
+ {
+ // Convert string to lower case
+ if(chr >= 'A' && chr <= 'Z')
+ {
+ chr = chr - 'A' + 'a';
+ }
+ name.append(1, static_cast<char>(chr));
+ }
+
+ chunk.Skip(4); // Object flags
+ return name;
+}
+
+
+// Read an entry from the import table.
+int32 ReadUMXImportTableEntry(FileReader &chunk)
+//----------------------------------------------
+{
+ ReadUMXIndex(chunk); // Class package
+ ReadUMXIndex(chunk); // Class name
+ chunk.Skip(4); // Package
+ return ReadUMXIndex(chunk); // Object name (offset into the name table)
+}
+
+
+// Read an entry from the export table.
+void ReadUMXExportTableEntry(FileReader &chunk, int32 &objClass, int32 &objOffset, int32 &objSize, int32 &objName)
+//----------------------------------------------------------------------------------------------------------------
+{
+ objClass = ReadUMXIndex(chunk); // Object class
+ ReadUMXIndex(chunk); // Object parent
+ chunk.Skip(4); // Internal package / group of the object
+ objName = ReadUMXIndex(chunk); // Object name (offset into the name table)
+ chunk.Skip(4); // Object flags
+ objSize = ReadUMXIndex(chunk);
+ if(objSize > 0)
+ {
+ objOffset = ReadUMXIndex(chunk);
+ }
+}
+
+
+bool CSoundFile::ReadUMX(FileReader &file)
+//----------------------------------------
+{
+ file.Rewind();
+ UMXFileHeader fileHeader;
+ if(!file.ReadConvertEndianness(fileHeader)
+ || fileHeader.magic != UMXFileHeader::magicBytes
+ || !file.Seek(fileHeader.nameOffset))
+ {
+ return false;
+ }
+
+ // Read name table
+ std::vector<std::string> names;
+ names.reserve(fileHeader.nameCount);
+ for(uint32 i = 0; i < fileHeader.nameCount; i++)
+ {
+ names.push_back(ReadUMXNameTableEntry(file, fileHeader.packageVersion));
+ }
+
+ // Read import table
+ if(!file.Seek(fileHeader.importOffset))
+ {
+ return false;
+ }
+
+ std::vector<int32> classes;
+ classes.reserve(fileHeader.importCount);
+ for(uint32 i = 0; i < fileHeader.importCount; i++)
+ {
+ int32 objName = ReadUMXImportTableEntry(file);
+ if(static_cast<size_t>(objName) < names.size())
+ {
+ classes.push_back(objName);
+ }
+ }
+
+ // Read export table
+ if(!file.Seek(fileHeader.exportOffset))
+ {
+ return false;
+ }
+
+ // Now we can be pretty sure that we're doing the right thing.
+ m_nType = MOD_TYPE_UMX;
+ m_nSamples = 0;
+ m_nInstruments = 0;
+
+ for(uint32 i = 0; i < fileHeader.exportCount; i++)
+ {
+ int32 objClass, objOffset, objSize, objName;
+ ReadUMXExportTableEntry(file, objClass, objOffset, objSize, objName);
+
+ if(objSize <= 0 || objClass >= 0)
+ {
+ continue;
+ }
+
+ // Look up object class name (we only want music and sounds).
+ objClass = -objClass - 1;
+ bool isMusic = false, isSound = false;
+ if(static_cast<size_t>(objClass) < classes.size())
+ {
+ const char *objClassName = names[classes[objClass]].c_str();
+ isMusic = !strcmp(objClassName, "music");
+ isSound = !strcmp(objClassName, "sound");
+ }
+ if(!isMusic && !isSound)
+ {
+ continue;
+ }
+
+ FileReader chunk = file.GetChunk(objOffset, objSize);
+
+ if(chunk.IsValid())
+ {
+ // Read object properties
+ size_t propertyName = static_cast<size_t>(ReadUMXIndex(chunk));
+ if(propertyName >= names.size() || strcmp(names[propertyName].c_str(), "none"))
{
- DWORD dwRipOfs = uscan;
- return ReadIT(lpStream + dwRipOfs, dwMemLength - dwRipOfs);
+ // Can't bother to implement property reading, as no UMX or UAX files I've seen so far use properties for the relevant objects.
+ // If it should be necessary to implement this, check CUnProperty.cpp in http://ut-files.com/index.php?dir=Utilities/&file=utcms_source.zip
+ ASSERT(false);
+ continue;
}
- // S3M
- if (dwScan == 0x4D524353)
+
+ if(fileHeader.packageVersion >= 120)
{
- DWORD dwRipOfs = uscan - 44;
- return ReadS3M(lpStream + dwRipOfs, dwMemLength - dwRipOfs);
- }
- // XM
- if (!_strnicmp((LPCSTR)(lpStream+uscan), "Extended Module", 15))
+ // UT2003 Packages
+ ReadUMXIndex(chunk);
+ chunk.Skip(8);
+ } else if(fileHeader.packageVersion >= 100)
{
- DWORD dwRipOfs = uscan;
- return ReadXM(lpStream + dwRipOfs, dwMemLength - dwRipOfs);
+ // AAO Packages
+ chunk.Skip(4);
+ ReadUMXIndex(chunk);
+ chunk.Skip(4);
+ } else if(fileHeader.packageVersion >= 62)
+ {
+ // UT Packages
+ // Mech8.umx and a few other UT tunes have packageVersion = 62.
+ // In CUnSound.cpp, the condition above reads "packageVersion >= 63" but if that is used, those tunes won't load properly.
+ ReadUMXIndex(chunk);
+ chunk.Skip(4);
+ } else
+ {
+ // Old Unreal Packagaes
+ ReadUMXIndex(chunk);
}
- // MOD
- if ((uscan > MODMAGIC_OFFSET) && (dwScan == '.K.M'))
+ int32 size = ReadUMXIndex(chunk);
+
+ FileReader fileChunk = chunk.GetChunk(size);
+ // TODO: Use FileReader for those file types
+ const BYTE *data = reinterpret_cast<const BYTE *>(fileChunk.GetRawData());
+
+ if(isMusic)
{
- DWORD dwRipOfs = uscan - MODMAGIC_OFFSET;
- return ReadMod(lpStream+dwRipOfs, dwMemLength-dwRipOfs);
+ // Read as module
+ if(ReadIT(data, fileChunk.GetLength())
+ || ReadXM(data, fileChunk.GetLength())
+ || ReadS3M(data, fileChunk.GetLength())
+ || ReadWav(data, fileChunk.GetLength())
+ || ReadMod(data, fileChunk.GetLength()))
+ {
+ return true;
+ }
+ } else if(isSound && GetNumSamples() + 1 < MAX_SAMPLES)
+ {
+ // Read as sample
+ if(ReadSampleFromFile(GetNumSamples() + 1, (LPBYTE)data, fileChunk.GetLength()))
+ {
+ m_nSamples++;
+ if(static_cast<size_t>(objName) < names.size())
+ {
+ strncpy(m_szNames[GetNumSamples()], names[objName].c_str(), MAX_SAMPLENAME - 1);
+ }
+ }
}
}
}
- return false;
+
+ if(m_nSamples != 0)
+ {
+ m_nChannels = 4;
+ Patterns.Insert(0, 64);
+ Order[0] = 0;
+ return true;
+ } else
+ {
+ m_nType = MOD_TYPE_NONE;
+ return false;
+ }
}
-
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-03-30 23:29:11 UTC (rev 1233)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-03-31 17:46:38 UTC (rev 1234)
@@ -638,7 +638,7 @@
&& (!ReadUlt(lpStream, dwMemLength))
&& (!ReadDMF(lpStream, dwMemLength))
&& (!ReadDSM(lpStream, dwMemLength))
- && (!ReadUMX(lpStream, dwMemLength))
+ && (!ReadUMX(file))
&& (!ReadAMF(lpStream, dwMemLength))
&& (!ReadPSM(file))
&& (!ReadPSM16(file))
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-03-30 23:29:11 UTC (rev 1233)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-03-31 17:46:38 UTC (rev 1234)
@@ -387,7 +387,7 @@
bool ReadMT2(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadPSM(FileReader &file);
bool ReadPSM16(FileReader &file);
- bool ReadUMX(const LPCBYTE lpStream, const DWORD dwMemLength);
+ bool ReadUMX(FileReader &file);
bool ReadMO3(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadGDM(FileReader &file);
bool ReadIMF(const LPCBYTE lpStream, const DWORD dwMemLength);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-03-31 23:06:13
|
Revision: 1235
http://modplug.svn.sourceforge.net/modplug/?rev=1235&view=rev
Author: saga-games
Date: 2012-03-31 23:06:04 +0000 (Sat, 31 Mar 2012)
Log Message:
-----------
[Ref] Rewrote MOD loader / saver.
[Ref] Rewrote MO3 loader.
[Ref] Added Order::ReadFromArray to read order items from fixed-size array.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/LOAD_AMF.CPP
trunk/OpenMPT/soundlib/LOAD_DSM.CPP
trunk/OpenMPT/soundlib/Load_669.cpp
trunk/OpenMPT/soundlib/Load_ams.cpp
trunk/OpenMPT/soundlib/Load_far.cpp
trunk/OpenMPT/soundlib/Load_it.cpp
trunk/OpenMPT/soundlib/Load_mdl.cpp
trunk/OpenMPT/soundlib/Load_med.cpp
trunk/OpenMPT/soundlib/Load_mo3.cpp
trunk/OpenMPT/soundlib/Load_mod.cpp
trunk/OpenMPT/soundlib/Load_mt2.cpp
trunk/OpenMPT/soundlib/Load_mtm.cpp
trunk/OpenMPT/soundlib/Load_ptm.cpp
trunk/OpenMPT/soundlib/Load_s3m.cpp
trunk/OpenMPT/soundlib/Load_stm.cpp
trunk/OpenMPT/soundlib/Load_umx.cpp
trunk/OpenMPT/soundlib/Load_xm.cpp
trunk/OpenMPT/soundlib/ModSequence.cpp
trunk/OpenMPT/soundlib/ModSequence.h
trunk/OpenMPT/soundlib/Sampleio.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
trunk/OpenMPT/soundlib/patternContainer.cpp
Modified: trunk/OpenMPT/soundlib/LOAD_AMF.CPP
===================================================================
--- trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2012-03-31 23:06:04 UTC (rev 1235)
@@ -238,7 +238,7 @@
#endif
p->command = 0;
}
- ConvertModCommand(p);
+ ConvertModCommand(*p);
pin += 4;
p++;
}
Modified: trunk/OpenMPT/soundlib/LOAD_DSM.CPP
===================================================================
--- trunk/OpenMPT/soundlib/LOAD_DSM.CPP 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/LOAD_DSM.CPP 2012-03-31 23:06:04 UTC (rev 1235)
@@ -110,7 +110,7 @@
m_nDefaultGlobalVolume = psong->globalvol << 2;
if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME)) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
m_nSamplePreAmp = psong->mastervol & 0x7F;
- Order.ReadAsByte(psong->orders, psong->numord, sizeof(psong->orders));
+ Order.ReadFromArray(psong->orders, psong->numord);
for (UINT iPan=0; iPan<16; iPan++)
{
@@ -199,7 +199,7 @@
}
m[ch].command = (BYTE)command;
m[ch].param = (BYTE)param;
- if (command) ConvertModCommand(&m[ch]);
+ if (command) ConvertModCommand(m[ch]);
}
} else
{
Modified: trunk/OpenMPT/soundlib/Load_669.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_669.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -125,7 +125,7 @@
ReadFixedLineLengthMessage(reinterpret_cast<const BYTE *>(fileHeader.songmessage), 108, 36, 0);
// Reading Orders
- Order.ReadAsByte(fileHeader.orders, 128, 128);
+ Order.ReadFromArray(fileHeader.orders);
m_nRestartPos = fileHeader.restartpos;
if(Order[m_nRestartPos] >= fileHeader.patterns) m_nRestartPos = 0;
Modified: trunk/OpenMPT/soundlib/Load_ams.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_ams.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_ams.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -251,7 +251,7 @@
{
m[ch].command = cmd;
m[ch].param = b2;
- ConvertModCommand(&m[ch]);
+ ConvertModCommand(m[ch]);
}
}
}
Modified: trunk/OpenMPT/soundlib/Load_far.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_far.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_far.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -116,7 +116,7 @@
dwMemPos += sizeof(FARHEADER2);
if (dwMemPos >= dwMemLength) return true;
- Order.ReadAsByte(pmh2->orders, pmh2->snglen, sizeof(pmh2->orders));
+ Order.ReadFromArray(pmh2->orders, pmh2->snglen);
m_nRestartPos = pmh2->loopto;
// Reading Patterns
dwMemPos += headerlen - (869 + pmh1->stlen);
Modified: trunk/OpenMPT/soundlib/Load_it.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_it.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_it.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -955,7 +955,7 @@
{
m[ch].command = cmd;
m[ch].param = param;
- S3MConvert(&m[ch], true);
+ S3MConvert(m[ch], true);
lastvalue[ch].command = m[ch].command;
lastvalue[ch].param = m[ch].param;
}
@@ -1382,17 +1382,17 @@
}
BYTE b = 0;
- UINT command = m->command;
- UINT param = m->param;
- UINT vol = 0xFF;
- UINT note = m->note;
- if (note) b |= 1;
- if ((note) && (note < NOTE_MIN_SPECIAL)) note--;
+ uint8 command = m->command;
+ uint8 param = m->param;
+ uint8 vol = 0xFF;
+ uint8 note = m->note;
+ if (note != NOTE_NONE) b |= 1;
+ if (m->IsNote()) note--;
if (note == NOTE_FADE && GetType() != MOD_TYPE_MPT) note = 0xF6;
if (m->instr) b |= 2;
if (m->volcmd)
{
- UINT volcmd = m->volcmd;
+ uint8 volcmd = m->volcmd;
switch(volcmd)
{
case VOLCMD_VOLUME: vol = m->vol; if (vol > 64) vol = 64; break;
@@ -1423,7 +1423,7 @@
if (vol != 0xFF) b |= 4;
if (command)
{
- S3MSaveConvert(&command, ¶m, true, compatibilityExport);
+ S3MSaveConvert(command, param, true, compatibilityExport);
if (command) b |= 8;
}
// Packing information
Modified: trunk/OpenMPT/soundlib/Load_mdl.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mdl.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mdl.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -339,7 +339,7 @@
else
m_nChannels = i+1;
}
- Order.ReadAsByte(pmib->seq, norders, sizeof(pmib->seq));
+ Order.ReadFromArray(pmib->seq, norders);
break;
// ME: song message
case 0x454D:
Modified: trunk/OpenMPT/soundlib/Load_med.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_med.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_med.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -632,7 +632,7 @@
UINT nbo = BigEndianW(pmsh->songlen);
if (nbo >= MAX_ORDERS) nbo = MAX_ORDERS-1;
if (!nbo) nbo = 1;
- Order.ReadAsByte(pmsh->playseq, nbo, nbo);
+ Order.ReadFromArray(pmsh->playseq, nbo);
playtransp = pmsh->playtransp;
} else
{
Modified: trunk/OpenMPT/soundlib/Load_mo3.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mo3.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mo3.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -15,26 +15,38 @@
#include "../mptrack/Mptrack.h"
#endif // MODPLUG_TRACKER
-// decode a MO3 file (returns the same "exit codes" as UNMO3.EXE, eg. 0=success)
+#ifndef NO_MO3_SUPPORT
+
+// Decode a MO3 file (returns the same "exit codes" as UNMO3.EXE, eg. 0=success)
// IN: data/len = MO3 data/len
// OUT: data/len = decoded data/len (if successful)
-typedef int (WINAPI * UNMO3_DECODE)(void **data, int *len);
-// free the data returned by UNMO3_Decode
-typedef void (WINAPI * UNMO3_FREE)(void *data);
+typedef int (WINAPI * UNMO3_DECODE)(const void **data, int *len);
+// Free the data returned by UNMO3_Decode
+typedef void (WINAPI * UNMO3_FREE)(const void *data);
+#endif // NO_MO3_SUPPORT
-bool CSoundFile::ReadMO3(LPCBYTE lpStream, const DWORD dwMemLength)
-//-----------------------------------------------------------------
+bool CSoundFile::ReadMO3(FileReader &file)
+//----------------------------------------
{
- // no valid MO3 file (magic bytes: "MO3")
- if(dwMemLength < 4 || lpStream[0] != 'M' || lpStream[1] != 'O' || lpStream[2] != '3')
+ file.Rewind();
+ const void *stream = file.GetRawData();
+ int length = file.GetLength();
+
+ // No valid MO3 file (magic bytes: "MO3")
+ if(file.GetLength() < 8 || !file.ReadMagic("MO3"))
+ {
return false;
+ }
#ifdef NO_MO3_SUPPORT
- /* As of August 2010, the format revision is 5; Versions > 31 are unlikely to exist in the next few years,
- so we will just ignore those if there's no UNMO3 library to tell us if the file is valid or not
- (avoid log entry with .MOD files that have a song name starting with "MO3" */
- if(lpStream[3] > 31) return false;
+ // As of April 2012, the format revision is 5; Versions > 31 are unlikely to exist in the next few years,
+ // so we will just ignore those if there's no UNMO3 library to tell us if the file is valid or not
+ // (avoid log entry with .MOD files that have a song name starting with "MO3".
+ if(file.ReadUint8() > 31)
+ {
+ return false;
+ }
#ifdef MODPLUG_TRACKER
if(m_pModDoc != nullptr) m_pModDoc->AddToLog(GetStrI18N(_TEXT("The file appears to be a MO3 file, but this OpenMPT build does not support loading MO3 files.")));
@@ -42,10 +54,8 @@
return false;
#else
- bool bResult = false; // result of trying to load the module, false == fail.
- int iLen = static_cast<int>(dwMemLength);
- void **mo3Stream = (void **)&lpStream;
+ bool result = false; // Result of trying to load the module, false == fail.
// try to load unmo3.dll dynamically.
#ifdef MODPLUG_TRACKER
@@ -56,7 +66,7 @@
#else
HMODULE unmo3 = LoadLibrary(_TEXT("unmo3.dll"));
#endif // MODPLUG_TRACKER
- if(unmo3 == NULL) // Didn't succeed.
+ if(unmo3 == nullptr) // Didn't succeed.
{
#ifdef MODPLUG_TRACKER
if(m_pModDoc != nullptr) m_pModDoc->AddToLog(GetStrI18N(_TEXT("Loading MO3 file failed because unmo3.dll could not be loaded.")));
@@ -67,30 +77,28 @@
UNMO3_DECODE UNMO3_Decode = (UNMO3_DECODE)GetProcAddress(unmo3, "UNMO3_Decode");
UNMO3_FREE UNMO3_Free = (UNMO3_FREE)GetProcAddress(unmo3, "UNMO3_Free");
- if(UNMO3_Decode != NULL && UNMO3_Free != NULL)
+ if(UNMO3_Decode != nullptr && UNMO3_Free != nullptr)
{
- if(UNMO3_Decode(mo3Stream, &iLen) == 0)
+ if(UNMO3_Decode(&stream, &length) == 0)
{
- /* if decoding was successful, mo3Stream and iLen will keep the new
- pointers now. */
+ // If decoding was successful, stream and length will keep the new pointers now.
- if(iLen > 0)
+ if(length > 0)
{
- bResult = true;
- if ((!ReadXM((const BYTE *)*mo3Stream, (DWORD)iLen))
- && (!ReadIT((const BYTE *)*mo3Stream, (DWORD)iLen))
- && (!ReadS3M((const BYTE *)*mo3Stream, (DWORD)iLen))
-#ifndef FASTSOUNDLIB
- && (!ReadMTM((const BYTE *)*mo3Stream, (DWORD)iLen))
-#endif // FASTSOUNDLIB
- && (!ReadMod((const BYTE *)*mo3Stream, (DWORD)iLen))) bResult = false;
+ FileReader unpackedFile(static_cast<const char*>(stream), length);
+
+ result = ReadXM(static_cast<const LPCBYTE>(stream), length)
+ || ReadIT(static_cast<const LPCBYTE>(stream), length)
+ || ReadS3M(static_cast<const LPCBYTE>(stream), length)
+ || ReadMTM(static_cast<const LPCBYTE>(stream), length)
+ || ReadMod(unpackedFile);
}
- UNMO3_Free(*mo3Stream);
+ UNMO3_Free(stream);
}
}
FreeLibrary(unmo3);
}
- return bResult;
+ return result;
#endif // NO_MO3_SUPPORT
}
Modified: trunk/OpenMPT/soundlib/Load_mod.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mod.cpp 2012-03-31 17:46:38 UTC (rev 1234)
+++ trunk/OpenMPT/soundlib/Load_mod.cpp 2012-03-31 23:06:04 UTC (rev 1235)
@@ -2,9 +2,8 @@
* Load_mod.cpp
* ------------
* Purpose: MOD / NST (ProTracker / NoiseTracker) module loader / saver
- * Notes : This code is OLD and HORRIBLE!
+ * Notes : (currently none)
* Authors: Olivier Lapicque
- * Adam Goode (endian and char fixes for PPC)
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
@@ -13,68 +12,59 @@
#include "stdafx.h"
#include "Loaders.h"
-#pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data"
-
-extern WORD ProTrackerPeriodTable[6*12];
-
-void CSoundFile::ConvertModCommand(ModCommand *m) const
+void CSoundFile::ConvertModCommand(ModCommand &m) const
//-----------------------------------------------------
{
- UINT command = m->command, param = m->param;
+ switch(m.command)
+ {
+ case 0x00: if(m.param) m.command = CMD_ARPEGGIO; break;
+ case 0x01: m.command = CMD_PORTAMENTOUP; break;
+ case 0x02: m.command = CMD_PORTAMENTODOWN; break;
+ case 0x03: m.command = CMD_TONEPORTAMENTO; break;
+ case 0x04: m.command = CMD_VIBRATO; break;
+ case 0x05: m.command = CMD_TONEPORTAVOL; if(m.param & 0xF0) m.param &= 0xF0; break;
+ case 0x06: m.command = CMD_VIBRATOVOL; if(m.param & 0xF0) m.param &= 0xF0; break;
+ case 0x07: m.command = CMD_TREMOLO; break;
+ case 0x08: m.command = CMD_PANNING8; break;
+ case 0x09: m.command = CMD_OFFSET; break;
+ case 0x0A: m.command = CMD_VOLUMESLIDE; if(m.param & 0xF0) m.param &= 0xF0; break;
+ case 0x0B: m.command = CMD_POSITIONJUMP; break;
+ case 0x0C: m.command = CMD_VOLUME; break;
+ case 0x0D: m.command = CMD_PATTERNBREAK; m.param = ((m.param >> 4) * 10) + (m.param & 0x0F); break;
+ case 0x0E: m.command = CMD_MODCMDEX; break;
+ case 0x0F: m.command = (m.param <= ((GetType() & (MOD_TYPE_MOD)) ? 0x20u : 0x1Fu)) ? CMD_SPEED : CMD_TEMPO;
+ if ((m.param == 0xFF) && (GetNumSamples() == 15) && (GetType() & MOD_TYPE_MOD)) m.command = CMD_NONE; break; //<rewbs> what the hell is this?! :) //<jojo> it's the "stop tune" command! :-P
- switch(command)
- {
- case 0x00: if (param) command = CMD_ARPEGGIO; break;
- case 0x01: command = CMD_PORTAMENTOUP; break;
- case 0x02: command = CMD_PORTAMENTODOWN; break;
- case 0x03: command = CMD_TONEPORTAMENTO; break;
- case 0x04: command = CMD_VIBRATO; break;
- case 0x05: command = CMD_TONEPORTAVOL; if (param & 0xF0) param &= 0xF0; break;
- case 0x06: command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break;
- case 0x07: command = CMD_TREMOLO; break;
- case 0x08: command = CMD_PANNING8; break;
- case 0x09: command = CMD_OFFSET; break;
- case 0x0A: command = CMD_VOLUMESLIDE; if (param & 0xF0) param &= 0xF0; break;
- case 0x0B: command = CMD_POSITIONJUMP; break;
- case 0x0C: command = CMD_VOLUME; break;
- case 0x0D: command = CMD_PATTERNBREAK; param = ((param >> 4) * 10) + (param & 0x0F); break;
- case 0x0E: command = CMD_MODCMDEX; break;
- case 0x0F: command = (param <= ((m_nType & (MOD_TYPE_MOD)) ? 0x20u : 0x1Fu)) ? CMD_SPEED : CMD_TEMPO;
- if ((param == 0xFF) && (m_nSamples == 15) && (m_nType & MOD_TYPE_MOD)) command = 0; break; //<rewbs> what the hell is this?! :) //<jojo> it's the "stop tune" command! :-P
// Extension for XM extended effects
- case 'G' - 55: command = CMD_GLOBALVOLUME; break; //16
- case 'H' - 55: command = CMD_GLOBALVOLSLIDE; if (param & 0xF0) param &= 0xF0; break;
- case 'K' - 55: command = CMD_KEYOFF; break;
- case 'L' - 55: command = CMD_SETENVPOSITION; break;
- case 'M' - 55: command = CMD_CHANNELVOLUME; break;
- case 'N' - 55: command = CMD_CHANNELVOLSLIDE; break;
- case 'P' - 55: command = CMD_PANNINGSLIDE; if (param & 0xF0) param &= 0xF0; break;
- case 'R' - 55: command = CMD_RETRIG; break;
- case 'T' - 55: command = CMD_TREMOR; break;
- case 'X' - 55: command = CMD_XFINEPORTAUPDOWN; break;
- case 'Y' - 55: command = CMD_PANBRELLO; break; //34
- case 'Z' - 55: command = CMD_MIDI; break; //35
- case '\\' - 56: command = CMD_SMOOTHMIDI; break; //rewbs.smoothVST: 36 - note: this is actually displayed as "-" in FT2, but seems to be doing nothing.
- //case ':' - 21: command = CMD_DELAYCUT; break; //37
- case '#' + 3: command = CMD_XPARAM; break; //rewbs.XMfixes - XParam is 38
- default: command = CMD_NONE;
+ case 'G' - 55: m.command = CMD_GLOBALVOLUME; break; //16
+ case 'H' - 55: m.command = CMD_GLOBALVOLSLIDE; if (m.param & 0xF0) m.param &= 0xF0; break;
+ case 'K' - 55: m.command = CMD_KEYOFF; break;
+ case 'L' - 55: m.command = CMD_SETENVPOSITION; break;
+ case 'M' - 55: m.command = CMD_CHANNELVOLUME; break;
+ case 'N' - 55: m.command = CMD_CHANNELVOLSLIDE; break;
+ case 'P' - 55: m.command = CMD_PANNINGSLIDE; if (m.param & 0xF0) m.param &= 0xF0; break;
+ case 'R' - 55: m.command = CMD_RETRIG; break;
+ case 'T' - 55: m.command = CMD_TREMOR; break;
+ case 'X' - 55: m.command = CMD_XFINEPORTAUPDOWN; break;
+ case 'Y' - 55: m.command = CMD_PANBRELLO; break; //34
+ case 'Z' - 55: m.command = CMD_MIDI; break; //35
+ case '\\' - 56: m.command = CMD_SMOOTHMIDI; break; //rewbs.smoothVST: 36 - note: this is actually displayed as "-" in FT2, but seems to be doing nothing.
+ //case ':' - 21: m.command = CMD_DELAYCUT; break; //37
+ case '#' + 3: m.command = CMD_XPARAM; break; //rewbs.XMfixes - Xm.param is 38
+ default: m.command = CMD_NONE;
}
- m->command = command;
- m->param = param;
}
-WORD CSoundFile::ModSaveCommand(const ModCommand *m, const bool bXM, const bool bCompatibilityExport) const
-//---------------------------------------------------------------------------------------------------------
+void CSoundFile::ModSaveCommand(uint8 &command, uint8 ¶m, bool toXM, bool compatibilityExport) const
+//------------------------------------------------------------------------------------------------------
{
- UINT command = m->command, param = m->param;
-
switch(command)
{
- case CMD_NONE: command = param = 0; break;
- case CMD_ARPEGGIO: command = 0; break;
+ case CMD_NONE: command = param = 0; break;
+ case CMD_ARPEGGIO: command = 0; break;
case CMD_PORTAMENTOUP:
- if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
+ if (GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
{
if ((param & 0xF0) == 0xE0) { command = 0x0E; param = ((param & 0x0F) >> 2) | 0x10; break; }
else if ((param & 0xF0) == 0xF0) { command = 0x0E; param &= 0x0F; param |= 0x10; break; }
@@ -82,7 +72,7 @@
command = 0x01;
break;
case CMD_PORTAMENTODOWN:
- if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
+ if(GetType() & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM|MOD_TYPE_MPT))
{
if ((param & 0xF0) == 0xE0) { command = 0x0E; param= ((param & 0x0F) >> 2) | 0x20; break; }
else if ((param & 0xF0) == 0xF0) { command = 0x0E; param &= 0x0F; param |= 0x20; break; }
@@ -104,7 +94,7 @@
}
else if(param == 0xA4) // surround
{
- if(bCompatibilityExport || !bXM)
+ if(compatibilityExport || !toXM)
{
command = param = 0;
}
@@ -122,8 +112,8 @@
case CMD_VOLUME: command = 0x0C; break;
case CMD_PATTERNBREAK: command = 0x0D; param = ((param / 10) << 4) | (param % 10); break;
case CMD_MODCMDEX: command = 0x0E; break;
- case CMD_SPEED: command = 0x0F; param = min(param, (bXM) ? 0x1Fu : 0x20u); break;
- case CMD_TEMPO: command = 0x0F; param = max(param, (bXM) ? 0x20u : 0x21u); break;
+ case CMD_SPEED: command = 0x0F; param = min(param, (toXM) ? 0x1Fu : 0x20u); break;
+ case CMD_TEMPO: command = 0x0F; param = max(param, (toXM) ? 0x20u : 0x21u); break;
case CMD_GLOBALVOLUME: command = 'G' - 55; break;
case CMD_GLOBALVOLSLIDE: command = 'H' - 55; break;
case CMD_KEYOFF: command = 'K' - 55; break;
@@ -134,31 +124,31 @@
case CMD_RETRIG: command = 'R' - 55; break;
case CMD_TREMOR: command = 'T' - 55; break;
case CMD_XFINEPORTAUPDOWN:
- if(bCompatibilityExport && (param & 0xF0) > 2) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
+ if(compatibilityExport && (param & 0xF0) > 2) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
command = param = 0;
else
command = 'X' - 55;
break;
case CMD_PANBRELLO:
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = 'Y' - 55;
break;
case CMD_MIDI:
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = 'Z' - 55;
break;
case CMD_SMOOTHMIDI: //rewbs.smoothVST: 36
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = '\\' - 56;
break;
case CMD_XPARAM: //rewbs.XMfixes - XParam is 38
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = '#' + 3;
@@ -171,7 +161,7 @@
case 0x30: command = 0x0E; param = (param & 0x0F) | 0x40; break;
case 0x40: command = 0x0E; param = (param & 0x0F) | 0x70; break;
case 0x90:
- if(bCompatibilityExport)
+ if(compatibilityExport)
command = param = 0;
else
command = 'X' - 55;
@@ -188,61 +178,168 @@
command = param = 0;
}
- // don't even think about saving XM effects in MODs...
- if(command > 0x0F && !bXM)
+ // Don't even think about saving XM effects in MODs...
+ if(command > 0x0F && !toXM)
+ {
command = param = 0;
-
- return (WORD)((command << 8) | (param));
+ }
}
#pragma pack(push, 1)
+// File Header
+struct MODFileHeader
+{
+ uint8 numOrders;
+ uint8 restartPos;
+ uint8 orderList[128];
+ char magic[4];
+
+ // Check if header magic equals a given string.
+ bool IsMagic(const char *otherMagic) const
+ {
+ return (*reinterpret_cast<const uint32 *>(magic) == *reinterpret_cast<const uint32 *>(otherMagic));
+ }
+};
+
+STATIC_ASSERT(sizeof(MODFileHeader) == 134);
+
+
+// Sample Header
struct MODSampleHeader
{
char name[22];
uint16 length;
uint8 finetune;
uint8 volume;
- uint16 loopstart;
- uint16 looplen;
-};
+ uint16 loopStart;
+ uint16 loopLength;
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesBE(length);
+ SwapBytesBE(loopStart);
+ SwapBytesBE(loopLength);
+ }
-struct MODHeader
-{
- uint8 nOrders;
- uint8 nRestartPos;
- uint8 Orders[128];
- char Magic[4];
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.uFlags = 0;
+ mptSmp.nLength = length * 2;
+ mptSmp.RelativeTone = 0;
+ mptSmp.nFineTune = MOD2XMFineTune(finetune & 0x0F);
+ mptSmp.nVolume = 4 * min(volume, 64);
+ mptSmp.nGlobalVol = 64;
+ mptSmp.nPan = 128;
+
+ SmpLength lStart = loopStart * 2;
+ SmpLength lLength = loopLength * 2;
+ // Fix loops
+ if(lLength > 2 && (lStart + lLength > mptSmp.nLength)
+ && (lStart / 2 + lLength <= mptSmp.nLength))
+ {
+ lStart /= 2;
+ }
+ mptSmp.nLoopStart = lStart;
+ mptSmp.nLoopEnd = lStart + lLength;
+
+ if (mptSmp.nLength == 2)
+ {
+ mptSmp.nLength = 0;
+ }
+
+ if (mptSmp.nLength)
+ {
+ if(mptSmp.nLoopStart >= mptSmp.nLength)
+ {
+ mptSmp.nLoopStart = mptSmp.nLength - 1;
+ }
+ if(mptSmp.nLoopEnd > mptSmp.nLength)
+ {
+ mptSmp.nLoopEnd = mptSmp.nLength;
+ }
+ if(mptSmp.nLoopStart > mptSmp.nLoopEnd || mptSmp.nLoopEnd < 4 || mptSmp.nLoopEnd - mptSmp.nLoopStart < 4)
+ {
+ mptSmp.nLoopStart = 0;
+ mptSmp.nLoopEnd = 0;
+ }
+
+ // Fix for most likely broken sample loops. This fixes super_sufm_-_new_life.mod which has a long sample which is looped from 0 to 4.
+ if(mptSmp.nLoopEnd <= 8 && mptSmp.nLoopStart == 0 && mptSmp.nLength > mptSmp.nLoopEnd)
+ {
+ mptSmp.nLoopEnd = 0;
+ }
+ if (mptSmp.nLoopEnd > mptSmp.nLoopStart)
+ {
+ mptSmp.uFlags |= CHN_LOOP;
+ }
+ }
+ }
+
+ // Convert OpenMPT's internal sample header to a MOD sample header.
+ SmpLength ConvertToMOD(const ModSample &mptSmp)
+ {
+ SmpLength writeLength = mptSmp.nLength;
+ // If the sample size is odd, we have to add a padding byte, as all sample sizes in MODs are even.
+ if((writeLength % 2) != 0)
+ {
+ writeLength++;
+ }
+ LimitMax(writeLength, SmpLength(0x1FFF0));
+
+ length = static_cast<uint16>(writeLength / 2);
+
+ if(mptSmp.RelativeTone < 0)
+ {
+ finetune = 0x08;
+ } else if(mptSmp.RelativeTone > 0)
+ {
+ finetune = 0x07;
+ } else
+ {
+ finetune = XM2MODFineTune(mptSmp.nFineTune);
+ }
+ volume = static_cast<uint8>(mptSmp.nVolume / 4);
+
+ loopStart = 0;
+ loopLength = 1;
+ if(mptSmp.uFlags & CHN_LOOP)
+ {
+ loopStart = static_cast<uint16>(mptSmp.nLoopStart / 2);
+ loopLength = static_cast<uint16>(max(1, (mptSmp.nLoopEnd - mptSmp.nLoopStart) / 2));
+ }
+
+ return writeLength;
+ }
};
+STATIC_ASSERT(sizeof(MODSampleHeader) == 30);
+
+
#pragma pack(pop)
-bool IsMagic(const char *s1, const char *s2)
-{
- return (*reinterpret_cast<const uint32 *>(s1) == *reinterpret_cast<const uint32 *>(s2));
-}
// Functor for fixing VBlank MODs and MODs with 7-bit panning
struct FixMODPatterns
//===================
{
- FixMODPatterns(bool bVBlank, bool bPanning)
+ FixMODPatterns(bool fixVBlank, bool fixPanning)
{
- this->bVBlank = bVBlank;
- this->bPanning = bPanning;
+ this->fixVBlank = fixVBlank;
+ this->fixPanning = fixPanning;
}
- void operator()(ModCommand& m)
+ void operator()(ModCommand &m)
{
// Fix VBlank MODs
- if(m.command == CMD_TEMPO && this->bVBlank)
+ if(m.command == CMD_TEMPO && this->fixVBlank)
{
m.command = CMD_SPEED;
}
// Fix MODs with 7-bit + surround panning
- if(m.command == CMD_PANNING8 && this->bPanning)
+ if(m.command == CMD_PANNING8 && this->fixPanning)
{
if(m.param == 0xA4)
{
@@ -255,244 +352,336 @@
}
}
- bool bVBlank, bPanning;
+ bool fixVBlank, fixPanning;
};
-bool CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength)
-//---------------------------------------------------------------
+bool CSoundFile::ReadMod(FileReader &file)
+//----------------------------------------
{
- char s[1024];
- DWORD dwMemPos, dwTotalSampleLen;
- const MODHeader *header;
- UINT nErr;
+ file.Rewind();
- if ((!lpStream) || (dwMemLength < 0x600)) return false;
- dwMemPos = 20;
+ MODFileHeader fileHeader;
+ if(!file.Seek(20 + sizeof(MODSampleHeader) * 31) || !file.Read(fileHeader))
+ {
+ return false;
+ }
+
m_nSamples = 31;
m_nChannels = 4;
- header = reinterpret_cast<const MODHeader *>(lpStream + dwMemPos + sizeof(MODSampleHeader) * 31);
+ // Check MOD Magic
+ if(fileHeader.IsMagic("M.K.") // ProTracker and compatible
+ || fileHeader.IsMagic("M!K!") // ProTracker (64+ patterns)
+ || fileHeader.IsMagic("M&K!") // NoiseTracker
+ || fileHeader.IsMagic("N.T.") // NoiseTracker
+ || fileHeader.IsMagic("FEST")) // jobbig.mod by Mahoney
+ {
+ m_nChannels = 4;
+ } else if(fileHeader.IsMagic("CD81") // Falcon
+ || fileHeader.IsMagic("OKTA") // Oktalyzer
+ || fileHeader.IsMagic("OCTA")) // Oktalyzer
+ {
+ m_nChannels = 8;
+ } else if((!memcmp(fileHeader.magic, "FLT", 3) || !memcmp(fileHeader.magic, "EXO", 3)) && fileHeader.magic[3] >= '4' && fileHeader.magic[3] <= '9')
+ {
+ // FLTx / EXOx - Startrekker by Exolon / Fairlight
+ m_nChannels = fileHeader.magic[3] - '0';
+ } else if(fileHeader.magic[0] >= '1' && fileHeader.magic[0] <= '9' && !memcmp(fileHeader.magic + 1, "CHN", 3))
+ {
+ // xCHN - Many trackers
+ m_nChannels = fileHeader.magic[0] - '0';
+ } else if(fileHeader.magic[0] >= '1' && fileHeader.magic[0] <= '9' && fileHeader.magic[1]>='0' && fileHeader.magic[1] <= '9'
+ && (!memcmp(fileHeader.magic + 2, "CH", 2) || !memcmp(fileHeader.magic + 2, "CN", 2)))
+ {
+ // xxCN / xxCH - Many trackers
+ m_nChannels = (fileHeader.magic[0] - '0') * 10 + fileHeader.magic[1] - '0';
+ } else if(!memcmp(fileHeader.magic, "TDZ", 3) && fileHeader.magic[3] >= '4' && fileHeader.magic[3] <= '9')
+ {
+ // TDZx - TakeTracker
+ m_nChannels = fileHeader.magic[3] - '0';
+ } else
+ {
+ // Ultimate SoundTracker MODs - no magic bytes
+ m_nSamples = 15;
+ }
- // Check Mod Magic
- memcpy(s, header->Magic, 4);
- if ((IsMagic(s, "M.K.")) || (IsMagic(s, "M!K!"))
- || (IsMagic(s, "M&K!")) || (IsMagic(s, "N.T.")) || (IsMagic(s, "FEST"))) m_nChannels = 4;
- else if ((IsMagic(s, "CD81")) || (IsMagic(s, "OKTA"))) m_nChannels = 8;
-
- else if (!memcmp(s, "FLT", 3) && s[3] >= '4' && s[3] <= '9') m_nChannels = s[3] - '0';
- else if (s[0] >= '1' && s[0] <= '9' && !memcmp(s + 1, "CHN", 3)) m_nChannels = s[0] - '0';
- else if (s[0] >= '1' && s[0] <= '9' && s[1]>='0' && s[1] <= '9' && (!memcmp(s + 2, "CH", 2) || !memcmp(s + 2, "CN", 2))) m_nChannels = (s[0] - '0') * 10 + s[1] - '0';
- else if (!memcmp(s, "TDZ", 3) && s[3] >= '4' && s[3] <= '9') m_nChannels = s[3] - '0';
- else m_nSamples = 15; // Ultimate SoundTracker MODs
-
LimitMax(m_nChannels, MAX_BASECHANNELS);
// Startrekker 8 channel mod (needs special treatment, see below)
- bool isFLT8 = IsMagic(s, "FLT8");
+ bool isFLT8 = fileHeader.IsMagic("FLT8");
// Only apply VBlank tests to M.K. (ProTracker) modules.
- const bool bMdKd = IsMagic(s, "M.K.");
+ const bool isMdKd = fileHeader.IsMagic("M.K.");
// Load Samples
- nErr = 0;
- dwTotalSampleLen = 0;
- for (UINT i=1; i<=m_nSamples; i++)
+ int m15Errors = 0;
+ size_t totalSampleLen = 0;
+
+ file.Seek(20);
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
- const MODSampleHeader *pms = reinterpret_cast<const MODSampleHeader *>(lpStream + dwMemPos);
- ModSample *psmp = &Samples[i];
- UINT loopstart, looplen;
+ MODSampleHeader sampleHeader;
+ if(!file.ReadConvertEndianness(sampleHeader))
+ {
+ return false;
+ }
+ sampleHeader.ConvertToMPT(Samples[smp]);
- StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[i], pms->name);
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[smp], sampleHeader.name);
- psmp->uFlags = 0;
- psmp->nLength = BigEndianW(pms->length)*2;
- dwTotalSampleLen += psmp->nLength;
- psmp->nFineTune = MOD2XMFineTune(pms->finetune & 0x0F);
- psmp->nVolume = 4*pms->volume;
- if (psmp->nVolume > 256) { psmp->nVolume = 256; nErr++; }
- psmp->nGlobalVol = 64;
- psmp->nPan = 128;
- loopstart = BigEndianW(pms->loopstart)*2;
- looplen = BigEndianW(pms->looplen)*2;
- // Fix loops
- if ((looplen > 2) && (loopstart+looplen > psmp->nLength)
- && (loopstart/2+looplen <= psmp->nLength))
+ totalSampleLen += Samples[smp].nLength;
+
+ // M15 Sanity checks
+ if(sampleHeader.volume > 256)
{
- loopstart /= 2;
+ m15Errors++;
}
- psmp->nLoopStart = loopstart;
- psmp->nLoopEnd = loopstart + looplen;
- if (psmp->nLength < 4) psmp->nLength = 0;
- if (psmp->nLength)
+ if(sampleHeader.length > 32768)
{
- UINT derr = 0;
- if (psmp->nLoopStart >= psmp->nLength) { psmp->nLoopSt...
[truncated message content] |
|
From: <sag...@us...> - 2012-05-17 12:00:24
|
Revision: 1273
http://modplug.svn.sourceforge.net/modplug/?rev=1273&view=rev
Author: saga-games
Date: 2012-05-17 12:00:13 +0000 (Thu, 17 May 2012)
Log Message:
-----------
[Fix] MDL Loader was severely broken (what have I been smoking / drinking / thinking?)
[Fix] MED Loader: Sample transpose should work as intended (at least for MMD1 modules, should it be any different for MMD0?), MMD2 order list works again as well.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_mdl.cpp
trunk/OpenMPT/soundlib/Load_med.cpp
Modified: trunk/OpenMPT/soundlib/Load_mdl.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mdl.cpp 2012-05-14 23:05:24 UTC (rev 1272)
+++ trunk/OpenMPT/soundlib/Load_mdl.cpp 2012-05-17 12:00:13 UTC (rev 1273)
@@ -517,6 +517,10 @@
continue;
}
+ if(m_nSamples < info->sampleIndex)
+ {
+ m_nSamples = info->sampleIndex;
+ }
ModSample &sample = Samples[info->sampleIndex];
StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[info->sampleIndex], info->name);
@@ -539,7 +543,7 @@
}
}
sample.nGlobalVol = 64;
- sample.nVolume = 256;
+ //sample.nVolume = 256;
if((sampleHeader->flags & 0x01))
{
Modified: trunk/OpenMPT/soundlib/Load_med.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_med.cpp 2012-05-14 23:05:24 UTC (rev 1272)
+++ trunk/OpenMPT/soundlib/Load_med.cpp 2012-05-17 12:00:13 UTC (rev 1273)
@@ -300,7 +300,7 @@
} else command = 0;
}
break;
- case 0x09: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break;
+ case 0x09: command = (param <= 0x20) ? CMD_SPEED : CMD_TEMPO; break;
case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break;
case 0x0F: // Set Tempo / Special
// F.00 = Pattern Break
@@ -619,7 +619,9 @@
pSmp->nVolume = (pmsh->sample[iSHdr].svol << 2);
pSmp->nGlobalVol = 64;
if (pSmp->nVolume > 256) pSmp->nVolume = 256;
- pSmp->RelativeTone = -12 * pmsh->sample[iSHdr].strans;
+ // Was: pSmp->RelativeTone = -12 * pmsh->sample[iSHdr].strans;
+ // But that breaks MMD1 modules (f.e. "94' summer.mmd1" from Modland)
+ pSmp->RelativeTone = pmsh->sample[iSHdr].strans;
pSmp->nPan = 128;
if (pSmp->nLoopEnd <= 2) pSmp->nLoopEnd = 0;
if (pSmp->nLoopEnd) pSmp->uFlags |= CHN_LOOP;
@@ -647,6 +649,7 @@
DWORD sectiontable = BigEndian(pmsh2->sectiontable);
if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1;
nOrders = 0;
+ Order.resize(0);
for (UINT iSection=0; iSection<nSections; iSection++)
{
UINT nplayseq = 0;
@@ -671,7 +674,7 @@
UINT n = BigEndianW(pmps->length);
if (pseq+n <= dwMemLength)
{
- Order.resize(nOrders++);
+ Order.resize(nOrders + n);
for (UINT i=0; i<n; i++)
{
WORD seqval = BigEndianW(pmps->seq[i]);
@@ -684,7 +687,6 @@
}
}
playtransp = pmsh2->playtransp;
- while (nOrders < MAX_ORDERS) Order[nOrders++] = Order.GetInvalidPatIndex();
}
// Reading Expansion structure
if (pmex)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-05-30 16:45:31
|
Revision: 1291
http://modplug.svn.sourceforge.net/modplug/?rev=1291&view=rev
Author: saga-games
Date: 2012-05-30 16:45:20 +0000 (Wed, 30 May 2012)
Log Message:
-----------
[Mod] Rewrote STM loader to use FileReader class. Also fixed some little issues here and there in the loader.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_stm.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Load_stm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_stm.cpp 2012-05-29 22:37:46 UTC (rev 1290)
+++ trunk/OpenMPT/soundlib/Load_stm.cpp 2012-05-30 16:45:20 UTC (rev 1291)
@@ -16,168 +16,253 @@
#pragma pack(push, 1)
-typedef struct tagSTMNOTE
+// STM sample header struct
+struct STMSampleHeader
{
- BYTE note;
- BYTE insvol;
- BYTE volcmd;
- BYTE cmdinf;
-} STMNOTE;
+ char filename[12]; // Can't have long comments - just filename comments :)
+ uint8 zero;
+ uint8 disk; // A blast from the past
+ uint16 offset; // ISA in memory when in ST 2
+ uint16 length; // Sample length
+ uint16 loopStart; // Loop start point
+ uint16 loopEnd; // Loop end point
+ uint8 volume; // Volume
+ uint8 reserved2;
+ uint16 sampleRate;
+ uint8 reserved3[6]; // Yet more of PSi's reserved crap
+ // Convert an STM sample header to OpenMPT's internal sample header.
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize();
+ StringFixer::ReadString<StringFixer::nullTerminated>(mptSmp.filename, filename);
-// Raw STM sampleinfo struct:
-typedef struct tagSTMSAMPLE
+ mptSmp.nC5Speed = sampleRate;
+ mptSmp.nVolume = Util::Min(volume * 4u, 256u);
+ mptSmp.nLength = length;
+ mptSmp.nLoopStart = loopStart;
+ mptSmp.nLoopEnd = loopEnd;
+
+ if(mptSmp.nLength < 2 || volume == 0)
+ {
+ // As WTF as the above condition might sound, it seems to make sense.
+ // The zero-volume samples in acidlamb.stm shouldn't be loaded, but they have an actual sample length.
+ mptSmp.nLength = 0;
+ }
+
+ if(mptSmp.nLoopStart < mptSmp.nLength
+ && mptSmp.nLoopEnd > mptSmp.nLoopStart
+ && mptSmp.nLoopEnd != 0xFFFF)
+ {
+ mptSmp.uFlags = CHN_LOOP;
+ mptSmp.nLoopEnd = Util::Min(mptSmp.nLoopEnd, mptSmp.nLength);
+ }
+ }
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopStart);
+ SwapBytesLE(loopEnd);
+ SwapBytesLE(sampleRate);
+ }
+};
+
+
+// STM file header
+struct STMFileHeader
{
- CHAR filename[14]; // Can't have long comments - just filename comments :)
- WORD reserved; // ISA in memory when in ST 2
- WORD length; // Sample length
- WORD loopbeg; // Loop start point
- WORD loopend; // Loop end point
- BYTE volume; // Volume
- BYTE reserved2; // More reserved crap
- WORD c2spd; // Good old c2spd
- BYTE reserved3[6]; // Yet more of PSi's reserved crap
-} STMSAMPLE;
+ char songname[20];
+ char trackername[8]; // !SCREAM! for ST 2.xx
+ uint8 dosEof; // 0x1A
+ uint8 filetype; // 1=song, 2=module (only 2 is supported, of course) :)
+ uint8 verMajor; // Like 2
+ uint8 verMinor; // "ditto"
+ uint8 initTempo; // initspeed= stm inittempo>>4
+ uint8 numPatterns; // number of patterns
+ uint8 globalVolume; // <- WoW! a RiGHT TRiANGLE =8*)
+ uint8 reserved[13]; // More of PSi's internal crap
+ STMSampleHeader samples[31]; // Sample headers
+ uint8 order[128]; // Order list
+};
-// Raw STM header struct:
-typedef struct tagSTMHEADER
+// Pattern note entry
+struct STMPatternEntry
{
- char songname[20];
- char trackername[8]; // !SCREAM! for ST 2.xx
- CHAR unused; // 0x1A
- CHAR filetype; // 1=song, 2=module (only 2 is supported, of course) :)
- CHAR ver_major; // Like 2
- CHAR ver_minor; // "ditto"
- BYTE inittempo; // initspeed= stm inittempo>>4
- BYTE numpat; // number of patterns
- BYTE globalvol; // <- WoW! a RiGHT TRiANGLE =8*)
- BYTE reserved[13]; // More of PSi's internal crap
- STMSAMPLE sample[31]; // STM sample data
- BYTE patorder[128]; // Docs say 64 - actually 128
-} STMHEADER;
+ uint8 note;
+ uint8 insvol;
+ uint8 volcmd;
+ uint8 cmdinf;
+};
+
#pragma pack(pop)
-bool CSoundFile::ReadSTM(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+bool CSoundFile::ReadSTM(FileReader &file)
+//----------------------------------------
{
- STMHEADER *phdr = (STMHEADER *)lpStream;
- DWORD dwMemPos = 0;
+ file.Rewind();
- if ((!lpStream) || (dwMemLength < sizeof(STMHEADER))) return false;
- if ((phdr->filetype != 2) || (phdr->unused != 0x1A)
- || ((_strnicmp(phdr->trackername, "!SCREAM!", 8))
- && (_strnicmp(phdr->trackername, "BMOD2STM", 8)))) return false;
+ STMFileHeader fileHeader;
+ if(!file.Read(fileHeader)
+ || fileHeader.filetype != 2
+ || fileHeader.dosEof != 0x1A
+ || (_strnicmp(fileHeader.trackername, "!SCREAM!", 8)
+ && _strnicmp(fileHeader.trackername, "BMOD2STM", 8)))
+ {
+ return false;
+ }
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], phdr->songname);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.songname);
// Read STM header
m_nType = MOD_TYPE_STM;
+ m_dwSongFlags = 0;
m_nSamples = 31;
m_nChannels = 4;
m_nInstruments = 0;
m_nMinPeriod = 64;
m_nMaxPeriod = 0x7FFF;
- m_nDefaultSpeed = phdr->inittempo >> 4;
- if (m_nDefaultSpeed < 1) m_nDefaultSpeed = 1;
+ m_nDefaultSpeed = fileHeader.initTempo >> 4;
+ if(m_nDefaultSpeed < 1) m_nDefaultSpeed = 1;
m_nDefaultTempo = 125;
- m_nDefaultGlobalVolume = phdr->globalvol << 2;
- if (m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME) m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
- Order.ReadFromArray(phdr->patorder);
+ m_nDefaultGlobalVolume = fileHeader.globalVolume * 4;
+ LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
+
// Setting up channels
- for (UINT nSet=0; nSet<4; nSet++)
+ for(CHANNELINDEX chn = 0; chn < 4; chn++)
{
- ChnSettings[nSet].dwFlags = 0;
- ChnSettings[nSet].nVolume = 64;
- ChnSettings[nSet].nPan = (nSet & 1) ? 0x40 : 0xC0;
+ ChnSettings[chn].dwFlags = 0;
+ ChnSettings[chn].nVolume = 64;
+ ChnSettings[chn].nPan = (chn & 1) ? 0x40 : 0xC0;
}
- // Reading samples
- for (UINT nIns=0; nIns<31; nIns++)
+
+ // Read samples
+ for(SAMPLEINDEX smp = 0; smp < 31; smp++)
{
- ModSample *pIns = &Samples[nIns+1];
- STMSAMPLE *pStm = &phdr->sample[nIns]; // STM sample data
+ fileHeader.samples[smp].ConvertEndianness();
+ fileHeader.samples[smp].ConvertToMPT(Samples[smp + 1]);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp + 1], fileHeader.samples[smp].filename);
+ }
- StringFixer::ReadString<StringFixer::nullTerminated>(pIns->filename, pStm->filename);
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[nIns + 1], pStm->filename);
-
- pIns->nC5Speed = LittleEndianW(pStm->c2spd);
- pIns->nGlobalVol = 64;
- pIns->nVolume = pStm->volume << 2;
- if (pIns->nVolume > 256) pIns->nVolume = 256;
- pIns->nLength = LittleEndianW(pStm->length);
- if ((pIns->nLength < 4) || (!pIns->nVolume)) pIns->nLength = 0;
- pIns->nLoopStart = LittleEndianW(pStm->loopbeg);
- pIns->nLoopEnd = LittleEndianW(pStm->loopend);
- if ((pIns->nLoopEnd > pIns->nLoopStart) && (pIns->nLoopEnd != 0xFFFF))
+ // Read order list
+ Order.ReadFromArray(fileHeader.order);
+ for(ORDERINDEX ord = 0; ord < 128; ord++)
+ {
+ if(Order[ord] >= 99)
{
- pIns->uFlags |= CHN_LOOP;
- pIns->nLoopEnd = min(pIns->nLoopEnd, pIns->nLength);
+ Order[ord] = Order.GetInvalidPatIndex();
}
}
- dwMemPos = sizeof(STMHEADER);
- for (UINT nOrd = 0; nOrd < 128; nOrd++) if (Order[nOrd] >= 99) Order[nOrd] = Order.GetInvalidPatIndex();
- UINT nPatterns = phdr->numpat;
- for (UINT nPat=0; nPat<nPatterns; nPat++)
+
+ for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++)
{
- if (dwMemPos + 64*4*4 > dwMemLength) return true;
- if(Patterns.Insert(nPat, 64))
- return true;
- ModCommand *m = Patterns[nPat];
- STMNOTE *p = (STMNOTE *)(lpStream + dwMemPos);
- for (UINT n=0; n<64*4; n++, p++, m++)
+ STMPatternEntry patternData[64 * 4];
+
+ if(Patterns.Insert(pat, 64) || !file.ReadArray(patternData))
{
- UINT note,ins,vol,cmd;
- // extract the various information from the 4 bytes that
- // make up a single note
- note = p->note;
- ins = p->insvol >> 3;
- vol = (p->insvol & 0x07) + (p->volcmd >> 1);
- cmd = p->volcmd & 0x0F;
- if ((ins) && (ins < 32)) m->instr = ins;
- // special values of [SBYTE0] are handled here ->
- // we have no idea if these strange values will ever be encountered
- // but it appears as though stms sound correct.
- if ((note == 0xFE) || (note == 0xFC)) m->note = 0xFE; else
- // if note < 251, then all three bytes are stored in the file
- if (note < 0xFC) m->note = (note >> 4)*12 + (note&0xf) + 37;
- if (vol <= 64) { m->volcmd = VOLCMD_VOLUME; m->vol = vol; }
- m->param = p->cmdinf;
- switch(cmd)
+ continue;
+ }
+
+ ModCommand *m = Patterns[pat];
+ ORDERINDEX breakPos = ORDERINDEX_INVALID;
+ ROWINDEX breakRow = 63; // Candidate row for inserting pattern break
+
+ for(size_t n = 0; n < 64 * 4; n++, m++)
+ {
+ const STMPatternEntry &entry = patternData[n];
+
+ if(entry.note == 0xFE || entry.note == 0xFC)
{
- // Axx set speed to xx
- case 1: m->command = CMD_SPEED; m->param >>= 4; break;
- // Bxx position jump
- case 2: m->command = CMD_POSITIONJUMP; break;
- // Cxx patternbreak to row xx
- case 3: m->command = CMD_PATTERNBREAK; m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F); break;
- // Dxy volumeslide
- case 4: m->command = CMD_VOLUMESLIDE; break;
- // Exy toneslide down
- case 5: m->command = CMD_PORTAMENTODOWN; break;
- // Fxy toneslide up
- case 6: m->command = CMD_PORTAMENTOUP; break;
- // Gxx Tone portamento,speed xx
- case 7: m->command = CMD_TONEPORTAMENTO; break;
- // Hxy vibrato
- case 8: m->command = CMD_VIBRATO; break;
- // Ixy tremor, ontime x, offtime y
- case 9: m->command = CMD_TREMOR; break;
- // Jxy arpeggio
- case 10: m->command = CMD_ARPEGGIO; break;
- // Kxy Dual command H00 & Dxy
- case 11: m->command = CMD_VIBRATOVOL; break;
- // Lxy Dual command G00 & Dxy
- case 12: m->command = CMD_TONEPORTAVOL; break;
- // Xxx amiga command 8xx
- case 0x18: m->command = CMD_PANNING8; break;
+ m->note = NOTE_NOTECUT;
+ } else if(entry.note < 0xFC)
+ {
+ m->note = (entry.note >> 4) * 12 + (entry.note & 0x0F) + 36 + NOTE_MIN;
+ }
+
+ m->instr = entry.insvol >> 3;
+ if(m->instr > 31)
+ {
+ m->instr = 0;
+ }
+
+ int8 vol = (entry.insvol & 0x07) | (entry.volcmd >> 1);
+ if(vol <= 64)
+ {
+ m->volcmd = VOLCMD_VOLUME;
+ m->vol = vol;
+ }
+
+ static const uint8 stmEffects[] =
+ {
+ CMD_NONE, CMD_SPEED, CMD_POSITIONJUMP, CMD_PATTERNBREAK, // .ABC
+ CMD_VOLUMESLIDE, CMD_PORTAMENTODOWN, CMD_PORTAMENTOUP, CMD_TONEPORTAMENTO, // DEFG
+ CMD_VIBRATO, CMD_TREMOR, CMD_ARPEGGIO, CMD_NONE, // HIJK
+ CMD_NONE, CMD_NONE, CMD_NONE, CMD_NONE, // LMNO
+ // KLMNO can be entered in the editor but don't do anything
+ };
+
+ m->command = stmEffects[entry.volcmd & 0x0F];
+ m->param = entry.cmdinf;
+
+ switch(m->command)
+ {
+ case CMD_SPEED:
+ // I don't know how Axx really works, but I do know that this
+ // isn't it. It does all sorts of mindbogglingly screwy things:
+ // 01 - very fast,
+ // 0F - very slow.
+ // 10 - fast again!
+ // I don't get it.
+ m->param >>= 4;
+ break;
+
+ case CMD_PATTERNBREAK:
+ m->param = (m->param & 0xF0) * 10 + (m->param & 0x0F);
+ if(breakRow > m->param)
+ {
+ breakRow = m->param;
+ }
+ break;
+
+ case CMD_POSITIONJUMP:
+ // This effect is also very weird.
+ // Bxx doesn't appear to cause an immediate break -- it merely
+ // sets the next order for when the pattern ends (either by
+ // playing it all the way through, or via Cxx effect)
+ breakPos = m->param;
+ breakRow = 63;
+ m->command = CMD_NONE;
+ break;
+
+ case CMD_TREMOR:
+ // this actually does something with zero values, and has no
+ // effect memory. which makes SENSE for old-effects tremor,
+ // but ST3 went and screwed it all up by adding an effect
+ // memory and IT followed that, and those are much more popular
+ // than STM so we kind of have to live with this effect being
+ // broken... oh well. not a big loss.
+ break;
+
default:
- m->command = m->param = 0;
+ // Anything not listed above is a no-op if there's no value.
+ // (ST2 doesn't have effect memory)
+ if(!m->param)
+ {
+ m->command= CMD_NONE;
+ }
+ break;
}
}
- dwMemPos += 64*4*4;
+
+ if(breakPos != ORDERINDEX_INVALID)
+ {
+ TryWriteEffect(pat, breakRow, CMD_POSITIONJUMP, breakPos, false, CHANNELINDEX_INVALID, false, weTryPreviousRow);
+ }
}
// Reading Samples
@@ -187,18 +272,20 @@
SampleIO::littleEndian,
SampleIO::signedPCM);
- for (UINT nSmp=1; nSmp<=31; nSmp++)
+ for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
{
- ModSample &sample = Samples[nSmp];
- dwMemPos = (dwMemPos + 15) & (~15);
+ ModSample &sample = Samples[smp];
if(sample.nLength)
{
- UINT nPos = ((UINT)phdr->sample[nSmp-1].reserved) << 4;
- if ((nPos >= sizeof(STMHEADER)) && (nPos <= dwMemLength) && (sample.nLength <= dwMemLength-nPos)) dwMemPos = nPos;
- if (dwMemPos < dwMemLength)
+ //size_t sampleOffset = fileHeader.samples[smp - 1].offset << 4;
+ //if(sampleOffset > sizeof(STMPatternEntry) && sampleOffset < file.GetLength())
+ //{
+ // file.Seek(sampleOffset);
+ //} else
{
- dwMemPos += sampleIO.ReadSample(sample, (LPSTR)(lpStream + dwMemPos),dwMemLength - dwMemPos);
+ file.Seek((file.GetPosition() + 15) & (~15));
}
+ sampleIO.ReadSample(sample, file);
}
}
return true;
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-05-29 22:37:46 UTC (rev 1290)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-05-30 16:45:20 UTC (rev 1291)
@@ -613,7 +613,7 @@
&& !ReadS3M(file)
&& !ReadWav(file)
#ifndef MODPLUG_BASIC_SUPPORT
- && !ReadSTM(lpStream, dwMemLength)
+ && !ReadSTM(file)
&& !ReadMed(lpStream, dwMemLength)
#ifndef FASTSOUNDLIB
&& !ReadMTM(lpStream, dwMemLength)
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-05-29 22:37:46 UTC (rev 1290)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-05-30 16:45:20 UTC (rev 1291)
@@ -382,7 +382,7 @@
bool ReadMod(FileReader &file);
bool ReadMed(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadMTM(const LPCBYTE lpStream, const DWORD dwMemLength);
- bool ReadSTM(const LPCBYTE lpStream, const DWORD dwMemLength);
+ bool ReadSTM(FileReader &file);
bool ReadIT(const LPCBYTE lpStream, const DWORD dwMemLength);
//bool ReadMPT(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadITProject(const LPCBYTE lpStream, const DWORD dwMemLength); // -> CODE#0023 -> DESC="IT project files (.itp)" -! NEW_FEATURE#0023
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-06-15 17:00:26
|
Revision: 1299
http://modplug.svn.sourceforge.net/modplug/?rev=1299&view=rev
Author: saga-games
Date: 2012-06-15 17:00:18 +0000 (Fri, 15 Jun 2012)
Log Message:
-----------
[Mod] Rewrote FAR loader.
[Imp] UMX Loader: Apparently, Galaxy Sound System supports a few more formats, so try loading those as well.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_far.cpp
trunk/OpenMPT/soundlib/Load_umx.cpp
trunk/OpenMPT/soundlib/SampleIO.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Load_far.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_far.cpp 2012-06-12 15:45:16 UTC (rev 1298)
+++ trunk/OpenMPT/soundlib/Load_far.cpp 2012-06-15 17:00:18 UTC (rev 1299)
@@ -3,8 +3,7 @@
* ------------
* Purpose: Farandole (FAR) module loader
* Notes : (currently none)
- * Authors: Olivier Lapicque
- * OpenMPT Devs
+ * Authors: OpenMPT Devs (partly inspired by Storlek's FAR loader from Schism Tracker)
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
@@ -12,274 +11,305 @@
#include "stdafx.h"
#include "Loaders.h"
-#pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data"
-#define FARFILEMAGIC 0xFE524146 // "FAR"
-
#pragma pack(push, 1)
-typedef struct FARHEADER1
+// FAR File Header
+struct FARFileHeader
{
- DWORD id; // file magic FAR=
- char songname[32]; // songname
- uint8 reserved[8];
- char eofMagic[3]; // 13,10,26
- WORD headerlen; // remaining length of header in bytes
- BYTE version; // 0xD1
- BYTE onoff[16];
- BYTE edit1[9];
- BYTE speed;
- BYTE panning[16];
- BYTE edit2[4];
- WORD stlen;
-} FARHEADER1;
+ uint8 magic[4];
+ char songName[40];
+ uint8 eof[3];
+ uint16 headerLength;
+ uint8 version;
+ uint8 onOff[16];
+ uint8 editingState[9]; // Stuff we don't care about
+ uint8 defaultSpeed;
+ uint8 chnPanning[16];
+ uint8 patternState[4]; // More stuff we don't care about
+ uint16 messageLength;
-typedef struct FARHEADER2
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(headerLength);
+ SwapBytesLE(messageLength);
+ }
+};
+
+STATIC_ASSERT(sizeof(FARFileHeader) == 98);
+
+
+struct FAROrderHeader
{
- BYTE orders[256];
- BYTE numpat;
- BYTE snglen;
- BYTE loopto;
- WORD patsiz[256];
-} FARHEADER2;
+ uint8 orders[256];
+ uint8 numPatterns; // supposed to be "number of patterns stored in the file"; apparently that's wrong
+ uint8 numOrders;
+ uint8 restartPos;
+ uint16 patternSize[256];
-typedef struct FARSAMPLE
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ for(size_t i = 0; i < CountOf(patternSize); i++)
+ {
+ SwapBytesLE(patternSize[i]);
+ }
+ }
+};
+
+STATIC_ASSERT(sizeof(FAROrderHeader) == 771);
+
+
+// FAR Sample header
+struct FARSampleHeader
{
- CHAR samplename[32];
- DWORD length;
- BYTE finetune;
- BYTE volume;
- DWORD reppos;
- DWORD repend;
- BYTE type;
- BYTE loop;
-} FARSAMPLE;
+ // Sample flags
+ enum SampleFlags
+ {
+ smp16Bit = 0x01,
+ smpLoop = 0x08,
+ };
+ char name[32];
+ uint32 length;
+ uint8 finetune;
+ uint8 volume;
+ uint32 loopStart;
+ uint32 loopEnd;
+ uint8 type;
+ uint8 loop;
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopStart);
+ SwapBytesLE(loopEnd);
+ }
+
+ // Convert sample header to OpenMPT's internal format.
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize();
+
+ mptSmp.nLength = length;
+ mptSmp.nLoopStart = loopStart;
+ mptSmp.nLoopEnd = loopEnd;
+ mptSmp.nC5Speed = 8363 * 2;
+ mptSmp.nVolume = volume * 16;
+
+ if(type & smp16Bit)
+ {
+ mptSmp.nLength /= 2;
+ mptSmp.nLoopStart /= 2;
+ mptSmp.nLoopEnd /= 2;
+ }
+
+ if((loop & 8) && mptSmp.nLoopEnd > mptSmp.nLoopStart)
+ {
+ mptSmp.uFlags |= CHN_LOOP;
+ }
+ }
+
+ // Retrieve the internal sample format flags for this sample.
+ SampleIO GetSampleFormat() const
+ {
+ return SampleIO(
+ (type & smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit,
+ SampleIO::mono,
+ SampleIO::littleEndian,
+ SampleIO::signedPCM);
+ }
+};
+
+STATIC_ASSERT(sizeof(FARSampleHeader) == 48);
+
#pragma pack(pop)
-bool CSoundFile::ReadFAR(const BYTE *lpStream, const DWORD dwMemLength)
-//---------------------------------------------------------------------
+bool CSoundFile::ReadFAR(FileReader &file)
+//----------------------------------------
{
- if(dwMemLength < sizeof(FARHEADER1))
- return false;
+ file.Rewind();
- FARHEADER1 farHeader;
- memcpy(&farHeader, lpStream, sizeof(FARHEADER1));
- FARHEADER1 *pmh1 = &farHeader;
- FARHEADER2 *pmh2 = 0;
- DWORD dwMemPos = sizeof(FARHEADER1);
- UINT headerlen;
- BYTE samplemap[8];
-
- if ((!lpStream) || (dwMemLength < 1024) || (pmh1->id != LittleEndian(FARFILEMAGIC))
- || (pmh1->eofMagic[0] != 13) || (pmh1->eofMagic[1] != 10) || (pmh1->eofMagic[2] != 26))
+ FARFileHeader fileHeader;
+ if(!file.ReadConvertEndianness(fileHeader)
+ || memcmp(fileHeader.magic, "FAR\xFE", 4) != 0
+ || memcmp(fileHeader.eof, "\x0D\x0A\x1A", 3)
+ || file.GetLength() < static_cast<size_t>(fileHeader.headerLength))
{
return false;
}
- headerlen = LittleEndianW(pmh1->headerlen);
- pmh1->stlen = LittleEndianW( pmh1->stlen ); /* inplace byteswap -- Toad */
- if ((headerlen >= dwMemLength) || (dwMemPos + pmh1->stlen + sizeof(FARHEADER2) >= dwMemLength)) return false;
// Globals
m_nType = MOD_TYPE_FAR;
m_nChannels = 16;
m_nInstruments = 0;
m_nSamples = 0;
- m_nSamplePreAmp = 0x20;
- m_nDefaultSpeed = pmh1->speed;
+ m_nSamplePreAmp = 32;
+ m_nDefaultSpeed = fileHeader.defaultSpeed;
m_nDefaultTempo = 80;
m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], pmh1->songname);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.songName);
- // Channel Setting
- for (UINT nchpan=0; nchpan<16; nchpan++)
+ // Read channel settings
+ for(CHANNELINDEX chn = 0; chn < 16; chn++)
{
- ChnSettings[nchpan].dwFlags = 0;
- ChnSettings[nchpan].nPan = ((pmh1->panning[nchpan] & 0x0F) << 4) + 8;
- ChnSettings[nchpan].nVolume = 64;
+ ChnSettings[chn].dwFlags = fileHeader.onOff[chn] ? 0 : CHN_MUTE;
+ ChnSettings[chn].nPan = ((fileHeader.chnPanning[chn] & 0x0F) << 4) + 8;
+ ChnSettings[chn].nVolume = 64;
}
- // Reading comment
- if (pmh1->stlen)
+
+ // Read song message
+ if(fileHeader.messageLength != 0)
{
- UINT szLen = pmh1->stlen;
- if (szLen > dwMemLength - dwMemPos) szLen = dwMemLength - dwMemPos;
- ReadFixedLineLengthMessage(lpStream + dwMemPos, szLen, 132, 0); // 132 characters per line... wow. :)
- dwMemPos += pmh1->stlen;
+ ReadFixedLineLengthMessage(file, fileHeader.messageLength, 132, 0); // 132 characters per line... wow. :)
}
- // Reading orders
- if (sizeof(FARHEADER2) > dwMemLength - dwMemPos) return true;
- FARHEADER2 farHeader2;
- memcpy(&farHeader2, lpStream + dwMemPos, sizeof(FARHEADER2));
- pmh2 = &farHeader2;
- dwMemPos += sizeof(FARHEADER2);
- if (dwMemPos >= dwMemLength) return true;
- Order.ReadFromArray(pmh2->orders, pmh2->snglen);
- m_nRestartPos = pmh2->loopto;
- // Reading Patterns
- dwMemPos += headerlen - (869 + pmh1->stlen);
- if (dwMemPos >= dwMemLength) return true;
-
- // byteswap pattern data.
- for(uint16 psfix = 0; psfix < 256; psfix++)
+ // Read orders
+ FAROrderHeader orderHeader;
+ if(!file.ReadConvertEndianness(orderHeader))
{
- pmh2->patsiz[psfix] = LittleEndianW( pmh2->patsiz[psfix] ) ;
+ return false;
}
- // end byteswap of pattern data
+ Order.ReadFromArray(orderHeader.orders, orderHeader.numOrders);
+ m_nRestartPos = orderHeader.restartPos;
- WORD *patsiz = (WORD *)pmh2->patsiz;
- for (UINT ipat=0; ipat<256; ipat++) if (patsiz[ipat])
+ file.Seek(fileHeader.headerLength);
+
+ // Pattern effect LUT
+ static const uint8 farEffects[] =
{
- UINT patlen = patsiz[ipat];
- if ((ipat >= MAX_PATTERNS) || (patsiz[ipat] < 2))
+ CMD_NONE,
+ CMD_PORTAMENTOUP,
+ CMD_PORTAMENTODOWN,
+ CMD_TONEPORTAMENTO,
+ CMD_RETRIG,
+ CMD_VIBRATO, // depth
+ CMD_VIBRATO, // speed
+ CMD_VOLUMESLIDE, // up
+ CMD_VOLUMESLIDE, // down
+ CMD_VIBRATO, // sustained (?)
+ CMD_NONE, // actually slide-to-volume
+ CMD_PANNING8,
+ CMD_S3MCMDEX, // note offset => note delay?
+ CMD_NONE, // fine tempo down
+ CMD_NONE, // fine tempo up
+ CMD_SPEED,
+ };
+
+ // Read patterns
+ for(PATTERNINDEX pat = 0; pat < 256; pat++)
+ {
+ if(!orderHeader.patternSize[pat])
{
- dwMemPos += patlen;
continue;
}
- if (dwMemPos + patlen >= dwMemLength) return true;
- UINT rows = (patlen - 2) >> 6;
- if (!rows)
+
+ FileReader patternChunk = file.GetChunk(orderHeader.patternSize[pat]);
+
+ // Calculate pattern length in rows (every event is 4 bytes, and we have 16 channels)
+ ROWINDEX numRows = (orderHeader.patternSize[pat] - 2) / (16 * 4);
+ if(!numRows || numRows > MAX_PATTERN_ROWS || Patterns.Insert(pat, numRows))
{
- dwMemPos += patlen;
continue;
}
- if (rows > 256) rows = 256;
- if (rows < 16) rows = 16;
- if(Patterns.Insert(ipat, rows)) return true;
- ModCommand *m = Patterns[ipat];
- UINT patbrk = lpStream[dwMemPos];
- const BYTE *p = lpStream + dwMemPos + 2;
- UINT max = rows*16*4;
- if (max > patlen-2) max = patlen-2;
- for (UINT len=0; len<max; len += 4, m++)
+
+ // Read break row and unused value
+ ROWINDEX breakRow = patternChunk.ReadUint8();
+ patternChunk.Skip(1);
+ if(breakRow > 0 && breakRow < numRows - 2)
{
- BYTE note = p[len];
- BYTE ins = p[len+1];
- BYTE vol = p[len+2];
- BYTE eff = p[len+3];
- if (note)
+ breakRow++;
+ } else
+ {
+ breakRow = ROWINDEX_INVALID;
+ }
+
+ // Read pattern data
+ for(ROWINDEX row = 0; row < numRows; row++)
+ {
+ PatternRow rowBase = Patterns[pat].GetRow(row);
+ for(CHANNELINDEX chn = 0; chn < 16; chn++)
{
- m->instr = ins + 1;
- m->note = note + 36;
- }
- if (vol & 0x0F)
- {
- m->volcmd = VOLCMD_VOLUME;
- m->vol = (vol & 0x0F) << 2;
- if (m->vol <= 4) m->vol = 0;
- }
- switch(eff & 0xF0)
- {
- // 1.x: Portamento Up
- case 0x10:
- m->command = CMD_PORTAMENTOUP;
- m->param = eff & 0x0F;
- break;
- // 2.x: Portamento Down
- case 0x20:
- m->command = CMD_PORTAMENTODOWN;
- m->param = eff & 0x0F;
- break;
- // 3.x: Tone-Portamento
- case 0x30:
- m->command = CMD_TONEPORTAMENTO;
- m->param = (eff & 0x0F) << 2;
- break;
- // 4.x: Retrigger
- case 0x40:
- m->command = CMD_RETRIG;
- m->param = 6 / (1+(eff&0x0F)) + 1;
- break;
- // 5.x: Set Vibrato Depth
- case 0x50:
- m->command = CMD_VIBRATO;
- m->param = (eff & 0x0F);
- break;
- // 6.x: Set Vibrato Speed
- case 0x60:
- m->command = CMD_VIBRATO;
- m->param = (eff & 0x0F) << 4;
- break;
- // 7.x: Vol Slide Up
- case 0x70:
- m->command = CMD_VOLUMESLIDE;
- m->param = (eff & 0x0F) << 4;
- break;
- // 8.x: Vol Slide Down
- case 0x80:
- m->command = CMD_VOLUMESLIDE;
- m->param = (eff & 0x0F);
- break;
- // A.x: Port to vol
- case 0xA0:
- m->volcmd = VOLCMD_VOLUME;
- m->vol = ((eff & 0x0F) << 2) + 4;
- break;
- // B.x: Set Balance
- case 0xB0:
- m->command = CMD_PANNING8;
- m->param = (eff & 0x0F) << 4;
- break;
- // F.x: Set Speed
- case 0xF0:
- m->command = CMD_SPEED;
- m->param = eff & 0x0F;
- break;
- default:
- if ((patbrk) && (patbrk+1 == (len >> 6)) && (patbrk+1 != rows-1))
+ ModCommand &m = rowBase[chn];
+
+ uint8 data[4];
+ patternChunk.ReadArray(data);
+
+ if(data[0] > 0 && data[0] < 85)
{
- m->command = CMD_PATTERNBREAK;
- patbrk = 0;
+ m.note = data[0] + 35 + NOTE_MIN;
+ m.instr = data[1] + 1;
}
+
+ if(data[2] & 0x0F)
+ {
+ m.volcmd = VOLCMD_VOLUME;
+ m.vol = (data[2] & 0x0F) << 2;
+ }
+
+ m.param = data[3] & 0x0F;
+
+ switch(data[3] >> 4)
+ {
+ case 0x03: // Porta to note
+ m.param <<= 2;
+ break;
+ case 0x04: // Retrig
+ m.param = 6 / (1 + (m.param & 0xf)) + 1; // ugh?
+ break;
+ case 0x06: // Vibrato speed
+ case 0x07: // Volume slide up
+ case 0x0B: // Panning
+ m.param *= 8;
+ break;
+ case 0x0A: // Volume-portamento (what!)
+ m.volcmd = VOLCMD_VOLUME;
+ m.vol = (m.param << 2) + 4;
+ break;
+ case 0x0C: // Note offset
+ m.param = 6 / (1 + (m.param & 0x0F)) + 1;
+ m.param |= 0x0D;
+ }
+ m.command = farEffects[data[3] >> 4];
}
}
- dwMemPos += patlen;
+
+ TryWriteEffect(pat, breakRow, CMD_PATTERNBREAK, 0, false, CHANNELINDEX_INVALID, false, weTryNextRow);
}
- // Reading samples
- if (dwMemPos + 8 >= dwMemLength) return true;
- memcpy(samplemap, lpStream+dwMemPos, 8);
- dwMemPos += 8;
- for(UINT ismp=0; ismp<64; ismp++) if (samplemap[ismp >> 3] & (1 << (ismp & 7)))
+
+ // Read samples
+ uint8 sampleMap[8]; // Sample usage bitset
+ file.ReadArray(sampleMap);
+
+ for(SAMPLEINDEX smp = 0; smp < 64; smp++)
{
- if (dwMemPos + sizeof(FARSAMPLE) > dwMemLength) return true;
- const FARSAMPLE *pfs = reinterpret_cast<const FARSAMPLE*>(lpStream + dwMemPos);
- dwMemPos += sizeof(FARSAMPLE);
- m_nSamples = ismp + 1;
+ if(!(sampleMap[smp >> 3] & (1 << (smp & 7))))
+ {
+ continue;
+ }
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[ismp + 1], pfs->samplename);
+ FARSampleHeader sampleHeader;
+ if(!file.ReadConvertEndianness(sampleHeader))
+ {
+ return true;
+ }
- ModSample &sample = Samples[ismp + 1];
- sample.Initialize();
+ m_nSamples = smp + 1;
+ ModSample &sample = Samples[m_nSamples];
- const DWORD length = LittleEndian(pfs->length);
- sample.nLength = length;
- sample.nLoopStart = LittleEndian(pfs->reppos);
- sample.nLoopEnd = LittleEndian(pfs->repend);
- sample.nC5Speed = 8363 * 2;
- sample.nVolume = pfs->volume << 4;
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[m_nSamples], sampleHeader.name);
- if((sample.nLength > 3) && (dwMemPos + 4 < dwMemLength))
+
+ sampleHeader.ConvertToMPT(sample);
+
+ if(sample.nLength)
{
- SampleIO sampleIO(
- SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::signedPCM);
-
- if(pfs->type & 1)
- {
- sampleIO |= SampleIO::_16bit;
- sample.nLength >>= 1;
- sample.nLoopStart >>= 1;
- sample.nLoopEnd >>= 1;
- }
- if ((pfs->loop & 8) && (sample.nLoopEnd > 4)) sample.uFlags |= CHN_LOOP;
-
- sampleIO.ReadSample(sample, (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
+ sampleHeader.GetSampleFormat().ReadSample(sample, file);
}
- dwMemPos += length;
}
return true;
}
Modified: trunk/OpenMPT/soundlib/Load_umx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_umx.cpp 2012-06-12 15:45:16 UTC (rev 1298)
+++ trunk/OpenMPT/soundlib/Load_umx.cpp 2012-06-15 17:00:18 UTC (rev 1299)
@@ -253,7 +253,7 @@
} else if(fileHeader.packageVersion >= 62)
{
// UT Packages
- // Mech8.umx and a few other UT tunes have packageVersion = 62.
+ // Mech8.umx and a few other UT tunes have packageVersion = 62.
// In CUnSound.cpp, the condition above reads "packageVersion >= 63" but if that is used, those tunes won't load properly.
ReadUMXIndex(chunk);
chunk.Skip(4);
@@ -265,7 +265,6 @@
int32 size = ReadUMXIndex(chunk);
FileReader fileChunk = chunk.GetChunk(size);
- // TODO: Use FileReader for those file types
const BYTE *data = reinterpret_cast<const BYTE *>(fileChunk.GetRawData());
if(isMusic)
@@ -275,11 +274,14 @@
|| ReadXM(data, fileChunk.GetLength())
|| ReadS3M(fileChunk)
|| ReadWav(fileChunk)
+ || ReadSTM(fileChunk)
+ || Read669(fileChunk)
+ || ReadFAR(fileChunk)
|| ReadMod(fileChunk))
{
return true;
}
- } else if(isSound && GetNumSamples() + 1 < MAX_SAMPLES)
+ } else if(isSound && GetNumSamples() < MAX_SAMPLES - 1)
{
// Read as sample
if(ReadSampleFromFile(GetNumSamples() + 1, (LPBYTE)data, fileChunk.GetLength()))
Modified: trunk/OpenMPT/soundlib/SampleIO.cpp
===================================================================
--- trunk/OpenMPT/soundlib/SampleIO.cpp 2012-06-12 15:45:16 UTC (rev 1298)
+++ trunk/OpenMPT/soundlib/SampleIO.cpp 2012-06-15 17:00:18 UTC (rev 1299)
@@ -28,7 +28,7 @@
size_t SampleIO::ReadSample(ModSample &sample, FileReader &file) const
//--------------------------------------------------------------------
{
- if(sample.nLength < 2 || !file.IsValid())
+ if(sample.nLength < 1 || !file.IsValid())
{
return 0;
}
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-06-12 15:45:16 UTC (rev 1298)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-06-15 17:00:18 UTC (rev 1299)
@@ -604,7 +604,7 @@
&& !ReadMDL(lpStream, dwMemLength)
&& !ReadDBM(lpStream, dwMemLength)
&& !Read669(file)
- && !ReadFAR(lpStream, dwMemLength)
+ && !ReadFAR(file)
&& !ReadAMS(lpStream, dwMemLength)
&& !ReadOKT(file)
&& !ReadPTM(lpStream, dwMemLength)
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-06-12 15:45:16 UTC (rev 1298)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-06-15 17:00:18 UTC (rev 1299)
@@ -390,7 +390,7 @@
bool ReadUlt(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadWav(FileReader &file);
bool ReadDSM(const LPCBYTE lpStream, const DWORD dwMemLength);
- bool ReadFAR(const LPCBYTE lpStream, const DWORD dwMemLength);
+ bool ReadFAR(FileReader &file);
bool ReadAMS(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadAMS2(const LPCBYTE lpStream, const DWORD dwMemLength);
bool ReadMDL(const LPCBYTE lpStream, const DWORD dwMemLength);
@@ -704,11 +704,10 @@
bool ReadMessage(const BYTE *data, const size_t length, enmLineEndings lineEnding, void (*pTextConverter)(char &) = nullptr);
bool ReadMessage(FileReader &file, const size_t length, enmLineEndings lineEnding, void (*pTextConverter)(char &) = nullptr)
{
- if(!file.CanRead(length))
- {
- return false;
- }
- return ReadMessage(reinterpret_cast<const BYTE*>(file.GetRawData()), length, lineEnding, pTextConverter);
+ size_t readLength = Util::Min(length, file.BytesLeft());
+ bool success = ReadMessage(reinterpret_cast<const BYTE*>(file.GetRawData()), readLength, lineEnding, pTextConverter);
+ file.Skip(readLength);
+ return success;
}
// Read comments with fixed line length from a mapped file.
@@ -721,11 +720,10 @@
bool ReadFixedLineLengthMessage(const BYTE *data, const size_t length, const size_t lineLength, const size_t lineEndingLength, void (*pTextConverter)(char &) = nullptr);
bool ReadFixedLineLengthMessage(FileReader &file, const size_t length, const size_t lineLength, const size_t lineEndingLength, void (*pTextConverter)(char &) = nullptr)
{
- if(!file.CanRead(length))
- {
- return false;
- }
- return ReadFixedLineLengthMessage(reinterpret_cast<const BYTE*>(file.GetRawData()), length, lineLength, lineEndingLength, pTextConverter);
+ size_t readLength = Util::Min(length, file.BytesLeft());
+ bool success = ReadFixedLineLengthMessage(reinterpret_cast<const BYTE*>(file.GetRawData()), readLength, lineLength, lineEndingLength, pTextConverter);
+ file.Skip(readLength);
+ return success;
}
private:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-06-15 22:01:30
|
Revision: 1301
http://modplug.svn.sourceforge.net/modplug/?rev=1301&view=rev
Author: saga-games
Date: 2012-06-15 22:01:22 +0000 (Fri, 15 Jun 2012)
Log Message:
-----------
[Ref] Rewrote J2B / PSM loaders to use chunk-based file reader.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_psm.cpp
trunk/OpenMPT/soundlib/load_j2b.cpp
Modified: trunk/OpenMPT/soundlib/Load_psm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_psm.cpp 2012-06-15 21:48:47 UTC (rev 1300)
+++ trunk/OpenMPT/soundlib/Load_psm.cpp 2012-06-15 22:01:22 UTC (rev 1301)
@@ -22,6 +22,7 @@
#include "stdafx.h"
#include "Loaders.h"
+#include "ChunkReader.h"
#ifdef MODPLUG_TRACKER
#include "../mptrack/moddoc.h"
#endif // MODPLUG_TRACKER
@@ -74,15 +75,22 @@
idDSMP = 0x504D5344,
};
+ typedef ChunkIdentifiers id_type;
+
uint32 id;
uint32 length;
- // Convert all multi-byte numeric values to current platform's endianness or vice versa.
- void ConvertEndianness()
+ size_t GetLength() const
{
- SwapBytesLE(id);
- SwapBytesLE(length);
+ uint32 l = length;
+ return SwapBytesLE(l);
}
+
+ id_type GetID() const
+ {
+ uint32 i = id;
+ return static_cast<id_type>(SwapBytesLE(i));
+ }
};
// Song Information
@@ -284,350 +292,337 @@
vector<PSMSubSong> subsongs;
bool subsongPanningDiffers = false; // do we have subsongs with different panning positions?
- while(file.BytesLeft())
+ ChunkReader chunkFile(file);
+ ChunkReader::ChunkList<PSMChunk> chunks = chunkFile.ReadChunks<PSMChunk>(1);
+
+ // "TITL" - Song Title
+ FileReader titleChunk = chunks.GetChunk(PSMChunk::idTITL);
+ titleChunk.ReadString<StringFixer::spacePadded>(m_szNames[0], titleChunk.GetLength());
+
+ // "SDFT" - Format info (song data starts here)
+ if(!chunks.GetChunk(PSMChunk::idSDFT).ReadMagic("MAINSONG"))
{
- // Go through the chunks
- PSMChunk chunkHead;
- if(!file.ReadConvertEndianness(chunkHead))
+ return false;
+ }
+
+ // "PBOD" - Pattern data of a single pattern
+ vector<FileReader> pattChunks = chunks.GetAllChunks(PSMChunk::idPBOD);
+ for(vector<FileReader>::iterator patternIter = pattChunks.begin(); patternIter != pattChunks.end(); patternIter++)
+ {
+ ChunkReader chunk(*patternIter);
+ if(chunk.GetLength() != chunk.ReadUint32LE() // Same value twice
+ || chunk.GetLength() < 8)
{
- break;
+ continue;
}
- FileReader chunk = file.GetChunk(chunkHead.length);
-
- switch(chunkHead.id)
+ // Pattern ID (something like "P0 " or "P13 ", or "PATT0 " in Sinaria) follows
+ char patternID[5];
+ if(!chunk.ReadString<StringFixer::spacePadded>(patternID, 4) || patternID[0] != 'P')
{
- case PSMChunk::idTITL: // "TITL" - Song Title
- chunk.ReadString<StringFixer::spacePadded>(m_szNames[0], chunk.GetLength());
- break;
+ continue;
+ }
+ if(!memcmp(patternID, "PATT", 4))
+ {
+ // New format has four additional bytes - read patternID again.
+ newFormat = true;
+ chunk.ReadString<StringFixer::spacePadded>(patternID, 4);
+ }
+ patternIDs.push_back(atoi(&patternID[newFormat ? 0 : 1]));
+ // We're going to read the rest of the pattern data later.
+ patternChunks.push_back(chunk.GetChunk(chunk.BytesLeft()));
- case PSMChunk::idSDFT: // "SDFT" - Format info (song data starts here)
- if(chunk.GetLength() != 8 || !chunk.ReadMagic("MAINSONG"))
- {
- return false;
- }
- break;
+ // Convert later as we have to know how many channels there are.
+ }
- case PSMChunk::idPBOD: // "PBOD" - Pattern data of a single pattern
- if(chunk.GetLength() != chunk.ReadUint32LE() // Same value twice
- || chunk.GetLength() < 8)
- {
- break;
- }
+ // "SONG" - Subsong information (channel count etc)
+ vector<FileReader> songChunks = chunks.GetAllChunks(PSMChunk::idSONG);
+ if(songChunks.empty())
+ {
+ return false;
+ }
- // Pattern ID (something like "P0 " or "P13 ", or "PATT0 " in Sinaria) follows
- char patternID[5];
- if(!chunk.ReadString<StringFixer::spacePadded>(patternID, 4) || patternID[0] != 'P')
- {
- break;
- }
- if(!memcmp(patternID, "PATT", 4))
- {
- // New format has four additional bytes - read patternID again.
- newFormat = true;
- chunk.ReadString<StringFixer::spacePadded>(patternID, 4);
- }
- patternIDs.push_back(atoi(&patternID[newFormat ? 0 : 1]));
- // We're going to read the rest of the pattern data later.
- patternChunks.push_back(chunk.GetChunk(chunk.BytesLeft()));
+ for(vector<FileReader>::iterator subsongIter = songChunks.begin(); subsongIter != songChunks.end(); subsongIter++)
+ {
+ ChunkReader chunk(*subsongIter);
+ PSMSongHeader songHeader;
+ if(!chunk.Read(songHeader))
+ {
+ return false;
+ }
+ if(songHeader.compression != 0x01)
+ {
+ // No compression for PSM files
+ return false;
+ }
+ // Subsongs *might* have different channel count
+ m_nChannels = Clamp(static_cast<CHANNELINDEX>(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS);
- // Convert later as we have to know how many channels there are.
- break;
+ PSMSubSong subsong;
+ subsong.restartPos = (ORDERINDEX)Order.size(); // restart order "offset": current orderlist length
+ StringFixer::ReadString<StringFixer::nullTerminated>(subsong.songName, songHeader.songType); // subsong name
- case PSMChunk::idSONG: // "SONG" - Subsong information (channel count etc)
+ // Read "Sub sub chunks"
+ ChunkReader::ChunkList<PSMChunk> subChunks = chunk.ReadChunks<PSMChunk>(1);
+
+ for(ChunkReader::ChunkList<PSMChunk>::iterator subChunkIter = subChunks.begin(); subChunkIter != subChunks.end(); subChunkIter++)
+ {
+ FileReader subChunk(subChunkIter->GetData());
+ PSMChunk subChunkHead = subChunkIter->GetHeader();
+
+ switch(subChunkHead.id)
{
- PSMSongHeader songHeader;
- if(!chunk.Read(songHeader))
+ case PSMChunk::idDATE: // "DATE" - Conversion date (YYMMDD)
+ if(subChunkHead.length == 6)
{
- break;
+ char cversion[7];
+ subChunk.ReadString<StringFixer::maybeNullTerminated>(cversion, 6);
+ int version = atoi(cversion);
+ // Sinaria song dates (just to go sure...)
+ if(version == 800211 || version == 940902 || version == 940903 ||
+ version == 940906 || version == 940914 || version == 941213)
+ newFormat = true;
}
- if(songHeader.compression != 0x01)
+ break;
+
+ case PSMChunk::idOPLH: // "OPLH" - Order list, channel + module settings
+ if(subChunkHead.length >= 9)
{
- // No compression for PSM files
- return false;
- }
- // Subsongs *might* have different channel count
- m_nChannels = Clamp(static_cast<CHANNELINDEX>(songHeader.numChannels), m_nChannels, MAX_BASECHANNELS);
+ // First two bytes = Number of chunks that follow
+ //uint16 totalChunks = subChunk.ReadUint16LE();
+ subChunk.Skip(2);
- PSMSubSong subsong;
- subsong.restartPos = (ORDERINDEX)Order.size(); // restart order "offset": current orderlist length
- StringFixer::ReadString<StringFixer::nullTerminated>(subsong.songName, songHeader.songType); // subsong name
+ // Now, the interesting part begins!
+ uint16 chunkCount = 0, firstOrderChunk = uint16_max;
- // Read "Sub sub chunks"
- FileReader subChunk = chunk.GetChunk(chunk.BytesLeft());
- while(subChunk.BytesLeft())
- {
- PSMChunk subChunkHead;
- if(!subChunk.ReadConvertEndianness(subChunkHead))
+ // "Sub sub sub chunks" (grrrr, silly format)
+ while(subChunk.BytesLeft())
{
- break;
- }
-
- switch(subChunkHead.id)
- {
- case PSMChunk::idDATE: // "DATE" - Conversion date (YYMMDD)
- if(subChunkHead.length == 6)
+ uint8 subChunkID = subChunk.ReadUint8();
+ if(!subChunkID)
{
- char cversion[7];
- subChunk.ReadString<StringFixer::maybeNullTerminated>(cversion, 6);
- int version = atoi(cversion);
- // Sinaria song dates (just to go sure...)
- if(version == 800211 || version == 940902 || version == 940903 ||
- version == 940906 || version == 940914 || version == 941213)
- newFormat = true;
+ // Last chunk was reached.
+ break;
}
- break;
- case PSMChunk::idOPLH: // "OPLH" - Order list, channel + module settings
- if(subChunkHead.length >= 9)
+ switch(subChunkID)
{
- // First two bytes = Number of chunks that follow
- //uint16 totalChunks = subChunk.ReadUint16LE();
- subChunk.Skip(2);
-
- // Now, the interesting part begins!
- uint16 chunkCount = 0, firstOrderChunk = uint16_max;
-
- // "Sub sub sub chunks" (grrrr, silly format)
- while(subChunk.BytesLeft())
+ case 0x01: // Order list item
+ // Pattern name follows - find pattern (this is the orderlist)
{
- uint8 subChunkID = subChunk.ReadUint8();
- if(!subChunkID)
+ char patternID[5]; // temporary
+ if(newFormat)
{
- // Last chunk was reached.
- break;
+ subChunk.Skip(4);
+ subChunk.ReadString<StringFixer::spacePadded>(patternID, 4);
+ } else
+ {
+ subChunk.Skip(1);
+ subChunk.ReadString<StringFixer::spacePadded>(patternID, 3);
}
+ uint32 pat = atoi(patternID);
- switch(subChunkID)
+ // seek which pattern has this ID
+ for(size_t i = 0; i < patternIDs.size(); i++)
{
- case 0x01: // Order list item
- // Pattern name follows - find pattern (this is the orderlist)
+ if(patternIDs[i] == pat)
{
- char patternID[5]; // temporary
- if(newFormat)
- {
- subChunk.Skip(4);
- subChunk.ReadString<StringFixer::spacePadded>(patternID, 4);
- } else
- {
- subChunk.Skip(1);
- subChunk.ReadString<StringFixer::spacePadded>(patternID, 3);
- }
- uint32 pat = atoi(patternID);
+ // found the right pattern, copy offset + start / end positions.
+ if(subsong.startOrder == ORDERINDEX_INVALID)
+ subsong.startOrder = static_cast<ORDERINDEX>(orderOffsets.size());
+ subsong.endOrder = static_cast<ORDERINDEX>(orderOffsets.size());
- // seek which pattern has this ID
- for(size_t i = 0; i < patternIDs.size(); i++)
- {
- if(patternIDs[i] == pat)
- {
- // found the right pattern, copy offset + start / end positions.
- if(subsong.startOrder == ORDERINDEX_INVALID)
- subsong.startOrder = static_cast<ORDERINDEX>(orderOffsets.size());
- subsong.endOrder = static_cast<ORDERINDEX>(orderOffsets.size());
-
- // every pattern in the order will be unique, so store the pointer + pattern ID
- orderOffsets.push_back(&patternChunks[i]);
- Order.Append(numPatterns);
- numPatterns++;
- break;
- }
- }
+ // every pattern in the order will be unique, so store the pointer + pattern ID
+ orderOffsets.push_back(&patternChunks[i]);
+ Order.Append(numPatterns);
+ numPatterns++;
+ break;
}
- // Decide whether this is the first order chunk or not (for finding out the correct restart position)
- if(firstOrderChunk == uint16_max) firstOrderChunk = chunkCount;
- break;
+ }
+ }
+ // Decide whether this is the first order chunk or not (for finding out the correct restart position)
+ if(firstOrderChunk == uint16_max) firstOrderChunk = chunkCount;
+ break;
- case 0x04: // Restart position
- {
- uint16 restartChunk = subChunk.ReadUint16LE();
- ORDERINDEX restartPosition = 0;
- if(restartChunk >= firstOrderChunk) restartPosition = static_cast<ORDERINDEX>(restartChunk - firstOrderChunk);
- subsong.restartPos += restartPosition;
- }
- break;
+ case 0x04: // Restart position
+ {
+ uint16 restartChunk = subChunk.ReadUint16LE();
+ ORDERINDEX restartPosition = 0;
+ if(restartChunk >= firstOrderChunk) restartPosition = static_cast<ORDERINDEX>(restartChunk - firstOrderChunk);
+ subsong.restartPos += restartPosition;
+ }
+ break;
- case 0x07: // Default Speed
- subsong.defaultSpeed = subChunk.ReadUint8();
- break;
+ case 0x07: // Default Speed
+ subsong.defaultSpeed = subChunk.ReadUint8();
+ break;
- case 0x08: // Default Tempo
- subsong.defaultTempo = subChunk.ReadUint8();
- break;
+ case 0x08: // Default Tempo
+ subsong.defaultTempo = subChunk.ReadUint8();
+ break;
- case 0x0C: // Sample map table (???)
- // Never seems to be different, so...
- if (subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0xFF ||
- subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0x00 ||
- subChunk.ReadUint8() != 0x01 || subChunk.ReadUint8() != 0x00)
- {
- return false;
- }
- break;
+ case 0x0C: // Sample map table (???)
+ // Never seems to be different, so...
+ if (subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0xFF ||
+ subChunk.ReadUint8() != 0x00 || subChunk.ReadUint8() != 0x00 ||
+ subChunk.ReadUint8() != 0x01 || subChunk.ReadUint8() != 0x00)
+ {
+ return false;
+ }
+ break;
- case 0x0D: // Channel panning table
+ case 0x0D: // Channel panning table
+ {
+ uint8 chn = subChunk.ReadUint8();
+ uint8 pan = subChunk.ReadUint8();
+ uint8 type = subChunk.ReadUint8();
+ if(chn < subsong.channelPanning.size())
+ {
+ switch(type)
{
- uint8 chn = subChunk.ReadUint8();
- uint8 pan = subChunk.ReadUint8();
- uint8 type = subChunk.ReadUint8();
- if(chn < subsong.channelPanning.size())
- {
- switch(type)
- {
- case 0: // use panning
- subsong.channelPanning[chn] = pan ^ 128;
- subsong.channelSurround[chn] = false;
- break;
+ case 0: // use panning
+ subsong.channelPanning[chn] = pan ^ 128;
+ subsong.channelSurround[chn] = false;
+ break;
- case 2: // surround
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = true;
- break;
+ case 2: // surround
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = true;
+ break;
- case 4: // center
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = false;
- break;
+ case 4: // center
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = false;
+ break;
- }
- if(subsongPanningDiffers == false && subsongs.size() > 0)
- {
- if(subsongs.back().channelPanning[chn] != subsong.channelPanning[chn]
- || subsongs.back().channelSurround[chn] != subsong.channelSurround[chn])
- subsongPanningDiffers = true;
- }
- }
}
- break;
-
- case 0x0E: // Channel volume table (0...255) - apparently always 255
+ if(subsongPanningDiffers == false && subsongs.size() > 0)
{
- uint8 chn = subChunk.ReadUint8();
- uint8 vol = subChunk.ReadUint8();
- if(chn < subsong.channelVolume.size())
- {
- subsong.channelVolume[chn] = (vol / 4) + 1;
- }
+ if(subsongs.back().channelPanning[chn] != subsong.channelPanning[chn]
+ || subsongs.back().channelSurround[chn] != subsong.channelSurround[chn])
+ subsongPanningDiffers = true;
}
- break;
-
- default: // How the hell should this happen? I've listened through almost all existing (original) PSM files. :)
- // anyway, in such cases, we have to quit as we don't know how big the chunk really is.
- return false;
-
}
- chunkCount++;
}
- // separate subsongs by "---" patterns
- orderOffsets.push_back(nullptr);
- Order.Append();
- }
- break;
+ break;
- case PSMChunk::idPPAN: // PPAN - Channel panning table (used in Sinaria)
- // In some Sinaria tunes, this is actually longer than 2 * channels...
- ASSERT(subChunkHead.length >= m_nChannels * 2u);
- for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
- {
- if(subChunk.BytesLeft() < 2)
+ case 0x0E: // Channel volume table (0...255) - apparently always 255
{
- break;
+ uint8 chn = subChunk.ReadUint8();
+ uint8 vol = subChunk.ReadUint8();
+ if(chn < subsong.channelVolume.size())
+ {
+ subsong.channelVolume[chn] = (vol / 4) + 1;
+ }
}
+ break;
- uint8 type = subChunk.ReadUint8();
- uint8 pan = subChunk.ReadUint8();
+ default: // How the hell should this happen? I've listened through almost all existing (original) PSM files. :)
+ // anyway, in such cases, we have to quit as we don't know how big the chunk really is.
+ return false;
- switch(type)
- {
- case 0: // use panning
- subsong.channelPanning[chn] = pan ^ 128;
- subsong.channelSurround[chn] = false;
- break;
-
- case 2: // surround
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = true;
- break;
-
- case 4: // center
- subsong.channelPanning[chn] = 128;
- subsong.channelSurround[chn] = false;
- break;
- }
}
+ chunkCount++;
+ }
+ // separate subsongs by "---" patterns
+ orderOffsets.push_back(nullptr);
+ Order.Append();
+ }
+ case PSMChunk::idPPAN: // PPAN - Channel panning table (used in Sinaria)
+ // In some Sinaria tunes, this is actually longer than 2 * channels...
+ ASSERT(subChunkHead.length >= m_nChannels * 2u);
+ for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
+ {
+ if(subChunk.BytesLeft() < 2)
+ {
break;
+ }
- case PSMChunk::idPATT: // PATT - Pattern list
- // We don't really need this.
- break;
+ uint8 type = subChunk.ReadUint8();
+ uint8 pan = subChunk.ReadUint8();
- case PSMChunk::idDSAM: // DSAM - Sample list
- // We don't need this either.
+ switch(type)
+ {
+ case 0: // use panning
+ subsong.channelPanning[chn] = pan ^ 128;
+ subsong.channelSurround[chn] = false;
break;
- default:
+ case 2: // surround
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = true;
break;
+ case 4: // center
+ subsong.channelPanning[chn] = 128;
+ subsong.channelSurround[chn] = false;
+ break;
}
}
+ break;
- // attach this subsong to the subsong list - finally, all "sub sub sub ..." chunks are parsed.
- subsongs.push_back(subsong);
+ case PSMChunk::idPATT: // PATT - Pattern list
+ // We don't really need this.
+ break;
+
+ case PSMChunk::idDSAM: // DSAM - Sample list
+ // We don't need this either.
+ break;
+
+ default:
+ break;
+
}
+ }
- break;
+ // attach this subsong to the subsong list - finally, all "sub sub sub ..." chunks are parsed.
+ subsongs.push_back(subsong);
+ }
- case PSMChunk::idDSMP: // DSMP - Samples
- if(!newFormat)
+ // // DSMP - Samples
+ vector<FileReader> sampleChunks = chunks.GetAllChunks(PSMChunk::idDSMP);
+ for(vector<FileReader>::iterator sampleIter = sampleChunks.begin(); sampleIter != sampleChunks.end(); sampleIter++)
+ {
+ FileReader chunk(*sampleIter);
+ if(!newFormat)
+ {
+ // Original header
+ PSMOldSampleHeader sampleHeader;
+ if(!chunk.ReadConvertEndianness(sampleHeader))
{
- // Original header
- PSMOldSampleHeader sampleHeader;
- if(!chunk.ReadConvertEndianness(sampleHeader))
- {
- break;
- }
+ continue;
+ }
- SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
- if(smp < MAX_SAMPLES)
- {
- m_nSamples = max(m_nSamples, smp);
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
-
- sampleHeader.ConvertToMPT(Samples[smp]);
- sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
- }
- } else
+ SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
+ if(smp < MAX_SAMPLES)
{
- // Sinaria uses a slightly different sample header
- PSMNewSampleHeader sampleHeader;
- if(!chunk.ReadConvertEndianness(sampleHeader))
- {
- break;
- }
+ m_nSamples = max(m_nSamples, smp);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
- SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
- if(smp < MAX_SAMPLES)
- {
- m_nSamples = max(m_nSamples, smp);
- StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
-
- sampleHeader.ConvertToMPT(Samples[smp]);
- sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
- }
+ sampleHeader.ConvertToMPT(Samples[smp]);
+ sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
}
+ } else
+ {
+ // Sinaria uses a slightly different sample header
+ PSMNewSampleHeader sampleHeader;
+ if(!chunk.ReadConvertEndianness(sampleHeader))
+ {
+ continue;
+ }
- break;
+ SAMPLEINDEX smp = static_cast<SAMPLEINDEX>(sampleHeader.sampleNumber + 1);
+ if(smp < MAX_SAMPLES)
+ {
+ m_nSamples = max(m_nSamples, smp);
+ StringFixer::ReadString<StringFixer::nullTerminated>(m_szNames[smp], sampleHeader.sampleName);
- default:
- break;
-
+ sampleHeader.ConvertToMPT(Samples[smp]);
+ sampleHeader.GetSampleFormat().ReadSample(Samples[smp], chunk);
+ }
}
}
- if(m_nChannels == 0 || subsongs.size() == 0)
- {
- return false;
- }
-
// Make the default variables of the first subsong global
m_nDefaultSpeed = subsongs[0].defaultSpeed;
m_nDefaultTempo = subsongs[0].defaultTempo;
@@ -1405,4 +1400,4 @@
}
return true;
-}
\ No newline at end of file
+}
Modified: trunk/OpenMPT/soundlib/load_j2b.cpp
===================================================================
--- trunk/OpenMPT/soundlib/load_j2b.cpp 2012-06-15 21:48:47 UTC (rev 1300)
+++ trunk/OpenMPT/soundlib/load_j2b.cpp 2012-06-15 22:01:22 UTC (rev 1301)
@@ -13,6 +13,7 @@
#include "stdafx.h"
#include "Loaders.h"
+#include "ChunkReader.h"
#ifndef ZLIB_WINAPI
#define ZLIB_WINAPI
#endif // ZLIB_WINAPI
@@ -20,7 +21,7 @@
// First off, a nice vibrato translation LUT.
-static const uint8 j2bAutoVibratoTrans[] =
+static const uint8 j2bAutoVibratoTrans[] =
{
VIB_SINE, VIB_SQUARE, VIB_RAMP_UP, VIB_RAMP_DOWN, VIB_RANDOM,
};
@@ -78,9 +79,23 @@
idAS__ = 0x20205341,
};
+ typedef ChunkIdentifiers id_type;
+
uint32 id; // See ChunkIdentifiers
uint32 length; // Chunk size without header
+ size_t GetLength() const
+ {
+ uint32 l = length;
+ return SwapBytesLE(l);
+ }
+
+ id_type GetID() const
+ {
+ uint32 i = id;
+ return static_cast<id_type>(SwapBytesLE(i));
+ }
+
// Convert all multi-byte numeric values to current platform's endianness or vice versa.
void ConvertEndianness()
{
@@ -283,6 +298,7 @@
// Convert sample header to OpenMPT's internal format.
void ConvertToMPT(AMFFInstrumentHeader &instrHeader, ModSample &mptSmp) const
{
+ mptSmp.Initialize();
mptSmp.nPan = pan * 4;
mptSmp.nVolume = volume * 4;
mptSmp.nGlobalVol = 64;
@@ -495,6 +511,7 @@
// Convert sample header to OpenMPT's internal format.
void ConvertToMPT(AMInstrumentHeader &instrHeader, ModSample &mptSmp) const
{
+ mptSmp.Initialize();
mptSmp.nPan = Util::Min(pan, static_cast<uint16>(32767)) * 256 / 32767;
mptSmp.nVolume = Util::Min(volume, static_cast<uint16>(32767)) * 256 / 32767;
mptSmp.nGlobalVol = 64;
@@ -539,8 +556,8 @@
// Convert RIFF AM(FF) pattern data to MPT pattern data.
-bool ConvertAMPattern(FileReader &chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile)
-//----------------------------------------------------------------------------------------
+bool ConvertAMPattern(FileReader chunk, PATTERNINDEX pat, bool isAM, CSoundFile &sndFile)
+//---------------------------------------------------------------------------------------
{
// Effect translation LUT
static const uint8 amEffTrans[] =
@@ -718,245 +735,227 @@
m_nSamples = 0;
m_nInstruments = 0;
- // go through all chunks now
- while(file.BytesLeft())
+ ChunkReader chunkFile(file);
+ // RIFF AM has a padding byte so that all chunks have an even size.
+ ChunkReader::ChunkList<AMFFRiffChunk> chunks = chunkFile.ReadChunks<AMFFRiffChunk>(isAM ? 2 : 1);
+
+ // "MAIN" - Song info (AMFF)
+ // "INIT" - Song info (AM)
+ FileReader chunk(chunks.GetChunk(isAM ? AMFFRiffChunk::idINIT : AMFFRiffChunk::idMAIN));
+ AMFFMainChunk mainChunk;
+ if(!chunk.IsValid() || !chunk.Read(mainChunk))
{
- AMFFRiffChunk chunkHeader;
- if(!file.ReadConvertEndianness(chunkHeader))
- {
- break;
- }
+ return false;
+ }
- FileReader chunk = file.GetChunk(chunkHeader.length);
- if(!chunk.IsValid())
- {
- continue;
- }
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], mainChunk.songname);
- switch(chunkHeader.id)
- {
- case AMFFRiffChunk::idMAIN: // "MAIN" - Song info (AMFF)
- case AMFFRiffChunk::idINIT: // "INIT" - Song info (AM)
- if((chunkHeader.id == AMFFRiffChunk::idMAIN && !isAM) || (chunkHeader.id == AMFFRiffChunk::idINIT && isAM))
- {
- AMFFMainChunk mainChunk;
- if(!chunk.Read(mainChunk))
- {
- break;
- }
+ m_dwSongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX;
+ if(!(mainChunk.flags & AMFFMainChunk::amigaSlides))
+ {
+ m_dwSongFlags |= SONG_LINEARSLIDES;
+ }
+ if(mainChunk.channels < 1 || !chunk.CanRead(mainChunk.channels))
+ {
+ return false;
+ }
+ m_nChannels = min(mainChunk.channels, MAX_BASECHANNELS);
+ m_nDefaultSpeed = mainChunk.speed;
+ m_nDefaultTempo = mainChunk.tempo;
+ m_nDefaultGlobalVolume = mainChunk.globalvolume * 2;
+ m_nSamplePreAmp = m_nVSTiVolume = 48;
+ m_nType = MOD_TYPE_J2B;
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], mainChunk.songname);
+ ASSERT(mainChunk.unknown == LittleEndian(0xFF0001C5) || mainChunk.unknown == LittleEndian(0x35800716) || mainChunk.unknown == LittleEndian(0xFF00FFFF));
- m_dwSongFlags = SONG_ITOLDEFFECTS | SONG_ITCOMPATGXX;
- if(!(mainChunk.flags & AMFFMainChunk::amigaSlides))
- {
- m_dwSongFlags |= SONG_LINEARSLIDES;
- }
- if(mainChunk.channels < 1 || !chunk.CanRead(mainChunk.channels))
- {
- return false;
- }
- m_nChannels = Util::Min(static_cast<CHANNELINDEX>(mainChunk.channels), MAX_BASECHANNELS);
- m_nDefaultSpeed = mainChunk.speed;
- m_nDefaultTempo = mainChunk.tempo;
- m_nDefaultGlobalVolume = mainChunk.globalvolume * 2;
- m_nSamplePreAmp = m_nVSTiVolume = 48;
- m_nType = MOD_TYPE_J2B;
+ // It seems like there's no way to differentiate between
+ // Muted and Surround channels (they're all 0xA0) - might
+ // be a limitation in mod2j2b.
+ for(CHANNELINDEX nChn = 0; nChn < m_nChannels; nChn++)
+ {
+ ChnSettings[nChn].nVolume = 64;
+ ChnSettings[nChn].nPan = 128;
- ASSERT(mainChunk.unknown == LittleEndian(0xFF0001C5) || mainChunk.unknown == LittleEndian(0x35800716) || mainChunk.unknown == LittleEndian(0xFF00FFFF));
+ uint8 pan = chunk.ReadUint8();
- // It seems like there's no way to differentiate between
- // Muted and Surround channels (they're all 0xA0) - might
- // be a limitation in mod2j2b.
- for(CHANNELINDEX nChn = 0; nChn < m_nChannels; nChn++)
- {
- ChnSettings[nChn].nVolume = 64;
- ChnSettings[nChn].nPan = 128;
+ if(isAM)
+ {
+ if(pan > 128)
+ ChnSettings[nChn].dwFlags = CHN_MUTE;
+ else
+ ChnSettings[nChn].nPan = pan * 2;
+ } else
+ {
+ if(pan >= 128)
+ ChnSettings[nChn].dwFlags = CHN_MUTE;
+ else
+ ChnSettings[nChn].nPan = pan * 4;
+ }
+ }
- uint8 pan = chunk.ReadUint8();
+ if(chunks.ChunkExists(AMFFRiffChunk::idORDR))
+ {
+ // "ORDR" - Order list
+ FileReader chunk(chunks.GetChunk(AMFFRiffChunk::idORDR));
+ uint8 numOrders = chunk.ReadUint8() + 1;
+ Order.ReadAsByte(chunk, numOrders);
+ }
- if(isAM)
- {
- if(pan > 128)
- ChnSettings[nChn].dwFlags = CHN_MUTE;
- else
- ChnSettings[nChn].nPan = pan * 2;
- } else
- {
- if(pan >= 128)
- ChnSettings[nChn].dwFlags = CHN_MUTE;
- else
- ChnSettings[nChn].nPan = pan * 4;
- }
- }
- }
- break;
+ // "PATT" - Pattern data for one pattern
+ vector<FileReader> pattChunks = chunks.GetAllChunks(AMFFRiffChunk::idPATT);
+ for(vector<FileReader>::iterator patternIter = pattChunks.begin(); patternIter != pattChunks.end(); patternIter++)
+ {
+ FileReader chunk(*patternIter);
+ PATTERNINDEX pat = chunk.ReadUint8();
+ size_t patternSize = chunk.ReadUint32LE();
+ ConvertAMPattern(chunk.GetChunk(patternSize), pat, isAM, *this);
+ }
- case AMFFRiffChunk::idORDR: // "ORDR" - Order list
+ if(!isAM)
+ {
+ // "INST" - Instrument (only in RIFF AMFF)
+ vector<FileReader> instChunks = chunks.GetAllChunks(AMFFRiffChunk::idINST);
+ for(vector<FileReader>::iterator instIter = instChunks.begin(); instIter != instChunks.end(); instIter++)
+ {
+ FileReader chunk(*instIter);
+ AMFFInstrumentHeader instrHeader;
+ if(!chunk.ReadConvertEndianness(instrHeader))
{
- uint8 numOrders = chunk.ReadUint8() + 1;
- Order.ReadAsByte(chunk, numOrders);
+ continue;
}
- break;
- case AMFFRiffChunk::idPATT: // "PATT" - Pattern data for one pattern
+ const INSTRUMENTINDEX nIns = instrHeader.index + 1;
+ if(nIns >= MAX_INSTRUMENTS)
+ continue;
+
+ if(Instruments[nIns] != nullptr)
+ delete Instruments[nIns];
+
+ try
{
- PATTERNINDEX pat = chunk.ReadUint8();
- size_t patternSize = chunk.ReadUint32LE();
- FileReader patternChunk = chunk.GetChunk(patternSize);
- ConvertAMPattern(patternChunk, pat,...
[truncated message content] |
|
From: <sag...@us...> - 2012-07-03 15:56:49
|
Revision: 1315
http://modplug.svn.sourceforge.net/modplug/?rev=1315&view=rev
Author: saga-games
Date: 2012-07-03 15:56:38 +0000 (Tue, 03 Jul 2012)
Log Message:
-----------
[Fix] XM Compatibility: First try to fix XM tremor... not quite correct yet, but at least Tito's ICANFLY.XM plays correctly now.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndmix.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-07-01 17:50:45 UTC (rev 1314)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-07-03 15:56:38 UTC (rev 1315)
@@ -464,7 +464,7 @@
case CMD_GLOBALVOLSLIDE:
if(IsCompatibleMode(TRK_IMPULSETRACKER | TRK_FASTTRACKER2))
{
- //IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
+ // IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
if (param) memory.oldGlbVolSlide[nChn] = param; else param = memory.oldGlbVolSlide[nChn];
} else
{
@@ -526,7 +526,7 @@
{
nNextOrder = nCurrentOrder + 1;
nNextRow = 0;
- if(IsCompatibleMode(TRK_FASTTRACKER2)) nNextRow = nNextPatStartRow; // FT2 E60 bug
+ if(IsCompatibleMode(TRK_FASTTRACKER2)) nNextRow = nNextPatStartRow; // FT2 E60 bug
nNextPatStartRow = 0;
}
@@ -561,7 +561,7 @@
m_bPositionChanged = true;
if(IsCompatibleMode(TRK_IMPULSETRACKER | TRK_FASTTRACKER2))
{
- //IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
+ // IT compatibility 16. Global volume slide params are stored per channel (FT2/IT)
for(CHANNELINDEX n = 0; n < GetNumChannels(); n++)
{
Chn[n].nOldGlobalVolSlide = memory.oldGlbVolSlide[n];
@@ -767,7 +767,7 @@
{
if ((!bPorta) || (!(GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) || (m_dwSongFlags & SONG_ITCOMPATGXX)
|| (!pChn->nLength) || ((pChn->dwFlags & CHN_NOTEFADE) && (!pChn->nFadeOutVol))
- //IT compatibility tentative fix: Reset envelopes when instrument changes.
+ // IT compatibility tentative fix: Reset envelopes when instrument changes.
|| (IsCompatibleMode(TRK_IMPULSETRACKER) && instrumentChanged))
{
pChn->dwFlags |= CHN_FASTVOLRAMP;
@@ -950,7 +950,7 @@
pChn->nFadeOutVol = 0;
}
- //IT compatibility tentative fix: Clear channel note memory.
+ // IT compatibility tentative fix: Clear channel note memory.
if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
pChn->nNote = pChn->nNewNote = NOTE_NONE;
@@ -1118,7 +1118,7 @@
pChn->dwFlags &= ~CHN_FILTER;
pChn->dwFlags |= CHN_FASTVOLRAMP;
- //IT compatibility 15. Retrigger will not be reset (Tremor doesn't store anything here, so we just don't reset this as well)
+ // IT compatibility 15. Retrigger will not be reset (Tremor doesn't store anything here, so we just don't reset this as well)
if(!IsCompatibleMode(TRK_IMPULSETRACKER))
{
// FT2 compatibility: FT2 also doesn't reset retrigger
@@ -1431,11 +1431,11 @@
ModChannel *p = &Chn[n];
// Copy Channel
*p = *pChn;
- p->dwFlags &= ~(CHN_VIBRATO|CHN_TREMOLO|CHN_PANBRELLO|CHN_MUTE|CHN_PORTAMENTO);
+ p->dwFlags &= ~(CHN_VIBRATO | CHN_TREMOLO | CHN_PANBRELLO | CHN_MUTE | CHN_PORTAMENTO);
//rewbs: Copy mute and FX status from master chan.
//I'd like to copy other flags too, but this would change playback behaviour.
- p->dwFlags |= (pChn->dwFlags & (CHN_MUTE|CHN_NOFX));
+ p->dwFlags |= (pChn->dwFlags & (CHN_MUTE | CHN_NOFX));
p->nMasterChn = nChn + 1;
p->nCommand = 0;
@@ -2181,17 +2181,17 @@
// IT compatibility 12. / 13. Tremor (using modified DUMB's Tremor logic here because of old effects - http://dumb.sf.net/)
if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
- if (param && !(m_dwSongFlags & SONG_ITOLDEFFECTS))
+ if(param && !(m_dwSongFlags & SONG_ITOLDEFFECTS))
{
// Old effects have different length interpretation (+1 for both on and off)
- if (param & 0xf0) param -= 0x10;
- if (param & 0x0f) param -= 0x01;
+ if(param & 0xF0) param -= 0x10;
+ if(param & 0x0F) param -= 0x01;
}
- pChn->nTremorCount |= 128; // set on/off flag
- }
- else
+ pChn->nTremorCount |= 0x80; // set on/off flag
+ } else if(IsCompatibleMode(TRK_FASTTRACKER2))
{
// XM Tremor. Logic is being processed in sndmix.cpp
+ pChn->nTremorCount |= 0x80; // set on/off flag
}
pChn->nCommand = CMD_TREMOR;
Modified: trunk/OpenMPT/soundlib/Sndmix.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndmix.cpp 2012-07-01 17:50:45 UTC (rev 1314)
+++ trunk/OpenMPT/soundlib/Sndmix.cpp 2012-07-03 15:56:38 UTC (rev 1315)
@@ -1014,25 +1014,57 @@
void CSoundFile::ProcessTremor(ModChannel *pChn, int &vol)
//--------------------------------------------------------
{
- if(pChn->nCommand == CMD_TREMOR)
+ if(IsCompatibleMode(TRK_FASTTRACKER2))
{
+ // Weird XM tremor. Not quite correct yet, but much better than non-compatible tremor.
+ if(pChn->nTremorCount & 0x80)
+ {
+ if(!(m_dwSongFlags & SONG_FIRSTTICK) && pChn->nCommand == CMD_TREMOR)
+ {
+ const uint8 onTime = (pChn->nTremorParam >> 4) + 1, totalTime = onTime + (pChn->nTremorParam & 0x0F) + 1;
+
+ if((pChn->nTremorCount & 0x3F) == totalTime)
+ {
+ // Reset tremor count after one full cycle
+ pChn->nTremorCount &= 0xC0;
+ }
+
+ if((pChn->nTremorCount & 0x3F) >= onTime)
+ {
+ // Volume Off
+ pChn->nTremorCount |= 0x40;
+ } else
+ {
+ // Volume On
+ pChn->nTremorCount &= ~0x40;
+ }
+
+ pChn->nTremorCount = (pChn->nTremorCount & 0xC0) | ((pChn->nTremorCount + 1) & 0x3F);
+ }
+
+ if((pChn->nTremorCount & 0xC0) == 0xC0)
+ {
+ vol = 0;
+ }
+ }
+ } else if(pChn->nCommand == CMD_TREMOR)
+ {
// IT compatibility 12. / 13.: Tremor
if(IsCompatibleMode(TRK_IMPULSETRACKER))
{
- if ((pChn->nTremorCount & 128) && pChn->nLength)
+ if((pChn->nTremorCount & 0x80) && pChn->nLength)
{
- if (pChn->nTremorCount == 128)
- pChn->nTremorCount = (pChn->nTremorParam >> 4) | 192;
- else if (pChn->nTremorCount == 192)
- pChn->nTremorCount = (pChn->nTremorParam & 0x0F) | 128;
+ if (pChn->nTremorCount == 0x80)
+ pChn->nTremorCount = (pChn->nTremorParam >> 4) | 0xC0;
+ else if (pChn->nTremorCount == 0xC0)
+ pChn->nTremorCount = (pChn->nTremorParam & 0x0F) | 0x80;
else
pChn->nTremorCount--;
}
- if ((pChn->nTremorCount & 192) == 128)
+ if((pChn->nTremorCount & 0xC0) == 0x80)
vol = 0;
- }
- else
+ } else
{
UINT ontime = pChn->nTremorParam >> 4;
UINT n = ontime + (pChn->nTremorParam & 0x0F); // Total tremor cycle time (On + Off)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-07-05 19:10:18
|
Revision: 1316
http://modplug.svn.sourceforge.net/modplug/?rev=1316&view=rev
Author: saga-games
Date: 2012-07-05 19:10:12 +0000 (Thu, 05 Jul 2012)
Log Message:
-----------
[Fix] Better fix for XM Tremor.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndmix.cpp
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-07-03 15:56:38 UTC (rev 1315)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-07-05 19:10:12 UTC (rev 1316)
@@ -1122,8 +1122,14 @@
if(!IsCompatibleMode(TRK_IMPULSETRACKER))
{
// FT2 compatibility: FT2 also doesn't reset retrigger
- if(!IsCompatibleMode(TRK_FASTTRACKER2)) pChn->nRetrigCount = 0;
- pChn->nTremorCount = 0;
+ if(!IsCompatibleMode(TRK_FASTTRACKER2))
+ {
+ pChn->nRetrigCount = 0;
+ pChn->nTremorCount = 0;
+ } else
+ {
+ pChn->nTremorCount = 0x20;
+ }
}
if (bResetEnv)
Modified: trunk/OpenMPT/soundlib/Sndmix.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndmix.cpp 2012-07-03 15:56:38 UTC (rev 1315)
+++ trunk/OpenMPT/soundlib/Sndmix.cpp 2012-07-05 19:10:12 UTC (rev 1316)
@@ -1016,33 +1016,29 @@
{
if(IsCompatibleMode(TRK_FASTTRACKER2))
{
- // Weird XM tremor. Not quite correct yet, but much better than non-compatible tremor.
+ // Weird XM tremor.
if(pChn->nTremorCount & 0x80)
{
if(!(m_dwSongFlags & SONG_FIRSTTICK) && pChn->nCommand == CMD_TREMOR)
{
- const uint8 onTime = (pChn->nTremorParam >> 4) + 1, totalTime = onTime + (pChn->nTremorParam & 0x0F) + 1;
-
- if((pChn->nTremorCount & 0x3F) == totalTime)
+ pChn->nTremorCount &= ~0x20;
+ if(pChn->nTremorCount == 0x80)
{
- // Reset tremor count after one full cycle
- pChn->nTremorCount &= 0xC0;
- }
-
- if((pChn->nTremorCount & 0x3F) >= onTime)
+ // Reached end of off-time
+ pChn->nTremorCount = (pChn->nTremorParam >> 4) | 0xC0;
+ } else if(pChn->nTremorCount == 0xC0)
{
- // Volume Off
- pChn->nTremorCount |= 0x40;
+ // Reached end of on-time
+ pChn->nTremorCount = (pChn->nTremorParam & 0x0F) | 0x80;
} else
{
- // Volume On
- pChn->nTremorCount &= ~0x40;
+ pChn->nTremorCount--;
}
-
- pChn->nTremorCount = (pChn->nTremorCount & 0xC0) | ((pChn->nTremorCount + 1) & 0x3F);
+
+ pChn->dwFlags |= CHN_FASTVOLRAMP;
}
- if((pChn->nTremorCount & 0xC0) == 0xC0)
+ if((pChn->nTremorCount & 0xE0) == 0x80)
{
vol = 0;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-07-09 20:27:25
|
Revision: 1323
http://modplug.svn.sourceforge.net/modplug/?rev=1323&view=rev
Author: saga-games
Date: 2012-07-09 20:27:18 +0000 (Mon, 09 Jul 2012)
Log Message:
-----------
[Imp] Portamento up/down effects are now implemented properly for VSTi: Memory and fine slides work. Todo: Maybe make it work with EAx/EBx/X1x/X2x (XM) as well.
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 2012-07-09 16:13:18 UTC (rev 1322)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-07-09 20:27:18 UTC (rev 1323)
@@ -2544,15 +2544,20 @@
//------------------------------------------------------------------------------------------------
{
ModChannel *pChn = &Chn[nChn];
- MidiPortamento(nChn, param); //Send midi pitch bend event if there's a plugin
if(param)
pChn->nOldPortaUpDown = param;
else
param = pChn->nOldPortaUpDown;
+ const bool doFineSlides = !doFinePortamentoAsRegular && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_STM));
+
+ // Process MIDI pitch bend for instrument plugins
+ MidiPortamento(nChn, param, doFineSlides);
+
if(GetType() == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning)
{
+ // Portamento for instruments with custom tuning
if(param >= 0xF0 && !doFinePortamentoAsRegular)
PortamentoFineMPT(pChn, param - 0xF0);
else
@@ -2560,7 +2565,7 @@
return;
}
- if (!doFinePortamentoAsRegular && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0))
+ if (doFineSlides && param >= 0xE0)
{
if (param & 0x0F)
{
@@ -2587,23 +2592,28 @@
//--------------------------------------------------------------------------------------------------
{
ModChannel *pChn = &Chn[nChn];
- MidiPortamento(nChn, -(int)param); //Send midi pitch bend event if there's a plugin
if(param)
pChn->nOldPortaUpDown = param;
else
param = pChn->nOldPortaUpDown;
- if(m_nType == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning)
+ const bool doFineSlides = !doFinePortamentoAsRegular && (GetType() & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_STM));
+
+ // Process MIDI pitch bend for instrument plugins
+ MidiPortamento(nChn, -static_cast<int>(param), doFineSlides);
+
+ if(GetType() == MOD_TYPE_MPT && pChn->pModInstrument && pChn->pModInstrument->pTuning)
{
+ // Portamento for instruments with custom tuning
if(param >= 0xF0 && !doFinePortamentoAsRegular)
- PortamentoFineMPT(pChn, -1*(param - 0xF0));
+ PortamentoFineMPT(pChn, -static_cast<int>(param - 0xF0));
else
- PortamentoMPT(pChn, -1*param);
+ PortamentoMPT(pChn, -static_cast<int>(param));
return;
}
- if (!doFinePortamentoAsRegular && (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_MPT|MOD_TYPE_STM)) && ((param & 0xF0) >= 0xE0))
+ if(doFineSlides && param >= 0xE0)
{
if (param & 0x0F)
{
@@ -2618,6 +2628,7 @@
}
return;
}
+
if (!(m_dwSongFlags & SONG_FIRSTTICK))
{
DoFreqSlide(pChn, int(param) * 4);
@@ -2625,14 +2636,39 @@
}
-void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param)
-//-----------------------------------------------------------
+// Send portamento commands to plugins
+void CSoundFile::MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides)
+//------------------------------------------------------------------------------
{
- IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn);
- if(plugin != nullptr)
+ int actualParam = abs(param);
+ int pitchBend = 0;
+
+ if(doFineSlides && actualParam >= 0xE0)
{
- plugin->MidiPitchBend(GetBestMidiChannel(nChn), param, 0);
+ if(m_dwSongFlags & SONG_FIRSTTICK)
+ {
+ // Extra fine slide...
+ pitchBend = (actualParam & 0x0F) * sgn(param);
+ if(actualParam >= 0xF0)
+ {
+ // ... or just a fine slide!
+ pitchBend *= 4;
+ }
+ }
+ } else
+ {
+ // Regular slide
+ pitchBend = param * 4;
}
+
+ if(pitchBend)
+ {
+ IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn);
+ if(plugin != nullptr)
+ {
+ plugin->MidiPitchBend(GetBestMidiChannel(nChn), pitchBend, 0);
+ }
+ }
}
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-07-09 16:13:18 UTC (rev 1322)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-07-09 20:27:18 UTC (rev 1323)
@@ -539,9 +539,9 @@
protected:
// Channel Effects
void UpdateS3MEffectMemory(ModChannel *pChn, UINT param) const;
- void PortamentoUp(CHANNELINDEX nChn, UINT param, const bool fineAsRegular = false);
- void PortamentoDown(CHANNELINDEX nChn, UINT param, const bool fineAsRegular = false);
- void MidiPortamento(CHANNELINDEX nChn, int param);
+ void PortamentoUp(CHANNELINDEX nChn, UINT param, const bool doFinePortamentoAsRegular = false);
+ void PortamentoDown(CHANNELINDEX nChn, UINT param, const bool doFinePortamentoAsRegular = false);
+ void MidiPortamento(CHANNELINDEX nChn, int param, bool doFineSlides);
void FinePortamentoUp(ModChannel *pChn, UINT param);
void FinePortamentoDown(ModChannel *pChn, UINT param);
void ExtraFinePortamentoUp(ModChannel *pChn, UINT param);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-07-14 18:18:55
|
Revision: 1326
http://modplug.svn.sourceforge.net/modplug/?rev=1326&view=rev
Author: saga-games
Date: 2012-07-14 18:18:48 +0000 (Sat, 14 Jul 2012)
Log Message:
-----------
[Fix] XM Compatibility export broke X1x and X2x (extra-fine portamento) commands.
[Fix] Overly long sample / instrument name strings found in IT files made with older versions of MPT are now read correctly.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/ITTools.cpp
trunk/OpenMPT/soundlib/Load_it.cpp
trunk/OpenMPT/soundlib/Load_mod.cpp
Modified: trunk/OpenMPT/soundlib/ITTools.cpp
===================================================================
--- trunk/OpenMPT/soundlib/ITTools.cpp 2012-07-13 18:45:15 UTC (rev 1325)
+++ trunk/OpenMPT/soundlib/ITTools.cpp 2012-07-14 18:18:48 UTC (rev 1326)
@@ -124,7 +124,7 @@
return;
}
- StringFixer::ReadString<StringFixer::spacePaddedNull>(mptIns.name, name);
+ StringFixer::ReadString<StringFixer::spacePadded>(mptIns.name, name);
StringFixer::ReadString<StringFixer::nullTerminated>(mptIns.filename, filename);
// Volume / Panning
@@ -148,8 +148,7 @@
if(note < 120)
{
mptIns.NoteMap[i] = note + 1u;
- }
- else
+ } else
{
mptIns.NoteMap[i] = static_cast<BYTE>(i + 1);
}
@@ -274,7 +273,7 @@
return 0;
}
- StringFixer::ReadString<StringFixer::spacePaddedNull>(mptIns.name, name);
+ StringFixer::ReadString<StringFixer::spacePadded>(mptIns.name, name);
StringFixer::ReadString<StringFixer::nullTerminated>(mptIns.filename, filename);
// Volume / Panning
@@ -342,8 +341,7 @@
if(note < 120)
{
mptIns.NoteMap[i] = note + 1u;
- }
- else
+ } else
{
mptIns.NoteMap[i] = static_cast<BYTE>(i + 1);
}
@@ -353,7 +351,7 @@
}
-// Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns true if instrument extension is actually necessary.
+// Convert OpenMPT's internal instrument representation to an ITInstrumentEx. Returns amount of bytes that need to be written to file.
size_t ITInstrumentEx::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile)
//-----------------------------------------------------------------------------------------------------------
{
@@ -459,8 +457,7 @@
flags |= ITSample::sample16Bit;
}
cvt = ITSample::cvtSignedSample;
- }
- else
+ } else
{
flags = 0x00;
}
Modified: trunk/OpenMPT/soundlib/Load_it.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_it.cpp 2012-07-13 18:45:15 UTC (rev 1325)
+++ trunk/OpenMPT/soundlib/Load_it.cpp 2012-07-14 18:18:48 UTC (rev 1326)
@@ -406,7 +406,7 @@
if ((itHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (itHeader.special & ITFileHeader::embedMIDIConfiguration)) m_dwSongFlags |= SONG_EMBEDMIDICFG;
if (itHeader.flags & ITFileHeader::extendedFilterRange) m_dwSongFlags |= SONG_EXFILTERRANGE;
- StringFixer::ReadString<StringFixer::spacePaddedNull>(m_szNames[0], itHeader.songname);
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[0], itHeader.songname);
// Global Volume
m_nDefaultGlobalVolume = itHeader.globalvol << 1;
@@ -759,7 +759,7 @@
{
size_t sampleOffset = pis->ConvertToMPT(Samples[nsmp + 1]);
- StringFixer::ReadString<StringFixer::spacePaddedNull>(m_szNames[nsmp + 1], pis->name);
+ StringFixer::ReadString<StringFixer::spacePadded>(m_szNames[nsmp + 1], pis->name);
lastSampleOffset = Util::Max(lastSampleOffset, sampleOffset + pis->GetSampleFormat(itHeader.cwtv).ReadSample(Samples[nsmp + 1], (LPSTR)(lpStream + sampleOffset), dwMemLength - sampleOffset));
}
Modified: trunk/OpenMPT/soundlib/Load_mod.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mod.cpp 2012-07-13 18:45:15 UTC (rev 1325)
+++ trunk/OpenMPT/soundlib/Load_mod.cpp 2012-07-14 18:18:48 UTC (rev 1326)
@@ -123,11 +123,9 @@
case CMD_PANNINGSLIDE: command = 'P' - 55; break;
case CMD_RETRIG: command = 'R' - 55; break;
case CMD_TREMOR: command = 'T' - 55; break;
- case CMD_XFINEPORTAUPDOWN:
- if(compatibilityExport && (param & 0xF0) > 2) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
- command = param = 0;
- else
- command = 'X' - 55;
+ case CMD_XFINEPORTAUPDOWN: command = 'X' - 55;
+ if(compatibilityExport && param >= 0x30) // X1x and X2x are legit, everything above are MPT extensions, which don't belong here.
+ param = 0; // Don't set command to 0 to indicate that there *was* some X command here...
break;
case CMD_PANBRELLO:
if(compatibilityExport)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-08-05 18:41:27
|
Revision: 1342
http://modplug.svn.sourceforge.net/modplug/?rev=1342&view=rev
Author: saga-games
Date: 2012-08-05 18:41:19 +0000 (Sun, 05 Aug 2012)
Log Message:
-----------
[Fix/Ref] Rewrote DSMI AMF loader and fixed a few more bugs in it while doing so. More AMF format revision (8-14) are supported now.
[Fix/Ref] Rewrote ASYLUM AMF loader. Made module mixer not ignore transpose in MOD mode anymore to simplify handling of tranpose in AMF files.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/LOAD_AMF.CPP
trunk/OpenMPT/soundlib/Snd_fx.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/LOAD_AMF.CPP
===================================================================
--- trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2012-08-05 18:38:07 UTC (rev 1341)
+++ trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2012-08-05 18:41:19 UTC (rev 1342)
@@ -2,7 +2,9 @@
* Load_amf.cpp
* ------------
* Purpose: AMF module loader
- * Notes : There are two types of AMF files, the ASYLUM Music Format and Advanced Music Format (DSM). Both module types are handled here.
+ * Notes : There are two types of AMF files, the ASYLUM Music Format (used in Crusader: No Remorse and Crusader: No Regret)
+ * and Advanced Music Format (DSMI / Digital Sound And Music Interface, used in various games such as Pinball World).
+ * Both module types are handled here.
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
@@ -12,417 +14,562 @@
#include "stdafx.h"
#include "Loaders.h"
-//#define AMFLOG
-#pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data"
-
#pragma pack(push, 1)
-typedef struct _AMFFILEHEADER
+// ASYLUM AMF File Header
+struct AsylumFileHeader
{
- UCHAR szAMF[3];
- UCHAR version;
- CHAR title[32];
- UCHAR numsamples;
- UCHAR numorders;
- USHORT numtracks;
- UCHAR numchannels;
-} AMFFILEHEADER;
+ char signature[32];
+ uint8 defaultSpeed;
+ uint8 defaultTempo;
+ uint8 numSamples;
+ uint8 numPatterns;
+ uint8 numOrders;
+ uint8 restartPos;
+};
-typedef struct _AMFSAMPLE
+STATIC_ASSERT(sizeof(AsylumFileHeader) == 38);
+
+
+// ASYLUM AMF Sample Header
+struct AsylumSampleHeader
{
- UCHAR type;
- CHAR samplename[32];
- CHAR filename[13];
- ULONG offset;
- ULONG length;
- USHORT c2spd;
- UCHAR volume;
-} AMFSAMPLE;
+ char name[22];
+ uint8 finetune;
+ uint8 defaultVolume;
+ int8 transpose;
+ uint32 length;
+ uint32 loopStart;
+ uint32 loopLength;
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopStart);
+ SwapBytesLE(loopLength);
+ }
+
+ // Convert an AMF sample header to OpenMPT's internal sample header.
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize();
+ mptSmp.nFineTune = MOD2XMFineTune(finetune);
+ mptSmp.nVolume = Util::Min(defaultVolume, uint8(64)) * 4u;
+ mptSmp.RelativeTone = transpose;
+ mptSmp.nLength = length;
+
+ if(loopLength > 2 && loopStart + loopLength <= length)
+ {
+ mptSmp.uFlags.set(CHN_LOOP);
+ mptSmp.nLoopStart = loopStart;
+ mptSmp.nLoopEnd = loopStart + loopLength;
+ }
+ }
+};
+
+STATIC_ASSERT(sizeof(AsylumSampleHeader) == 37);
+
#pragma pack(pop)
-#ifdef AMFLOG
-extern void Log(LPCSTR, ...);
-#endif
+bool CSoundFile::ReadAMF_Asylum(FileReader &file)
+//-----------------------------------------------
+{
+ file.Rewind();
-void AMF_Unpack(ModCommand *pPat, const BYTE *pTrack, UINT nRows, UINT nChannels)
-//-------------------------------------------------------------------------------
-{
- UINT lastinstr = 0;
- UINT nTrkSize = LittleEndianW(*(USHORT *)pTrack);
- nTrkSize += (UINT)pTrack[2] << 16;
- pTrack += 3;
- while (nTrkSize--)
+ AsylumFileHeader fileHeader;
+ if(!file.Read(fileHeader)
+ || strncmp(fileHeader.signature, "ASYLUM Music Format V1.0", 25)
+ || fileHeader.numSamples > 64
+ || file.BytesLeft() < 256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns)
{
- UINT row = pTrack[0];
- UINT cmd = pTrack[1];
- UINT arg = pTrack[2];
- if (row >= nRows) break;
- ModCommand *m = pPat + row * nChannels;
- if (cmd < 0x7F) // note+vol
+ return false;
+ }
+
+ m_nType = MOD_TYPE_AMF0;
+ m_nChannels = 8;
+ m_nInstruments = 0;
+ m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
+ m_nDefaultSpeed = fileHeader.defaultSpeed;
+ m_nDefaultTempo = fileHeader.defaultTempo;
+ m_nSamples = fileHeader.numSamples;
+ m_nRestartPos = 0;
+ if(fileHeader.restartPos < fileHeader.numOrders)
+ {
+ m_nRestartPos = fileHeader.restartPos;
+ }
+ m_szNames[0][0] = '\0';
+
+ Order.ReadAsByte(file, fileHeader.numOrders);
+ file.Skip(256 - fileHeader.numOrders);
+
+ // Read Sample Headers
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
+ {
+ AsylumSampleHeader sampleHeader;
+ file.ReadConvertEndianness(sampleHeader);
+ sampleHeader.ConvertToMPT(Samples[smp]);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sampleHeader.name);
+ }
+
+ file.Skip((64 - fileHeader.numSamples) * sizeof(AsylumSampleHeader));
+
+ // Read Patterns
+ for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++)
+ {
+ if(Patterns.Insert(pat, 64))
{
- m->note = cmd+1;
- // Does the next line make sense? I can't find any documents on this format, nor do any other players seem to handle this problem (or I'm overlooking something). However, the tunes in Pinball World seem to play a lot better with this, and it kind of looks right.
- if(arg != 0xFF)
- {
- if (!m->instr) m->instr = lastinstr;
- m->volcmd = VOLCMD_VOLUME;
- m->vol = arg;
- }
- } else
- if (cmd == 0x7F) // duplicate row
+ file.Skip(64 * 4 * 8);
+ continue;
+ }
+
+ ModCommand *p = Patterns[pat];
+ for(size_t i = 0; i < 8 * 64; i++, p++)
{
- signed char rdelta = (signed char)arg;
- int rowsrc = (int)row + (int)rdelta;
- if ((rowsrc >= 0) && (rowsrc < (int)nRows)) *m = pPat[rowsrc*nChannels];
- } else
- if (cmd == 0x80) // instrument
- {
- m->instr = arg+1;
- lastinstr = m->instr;
- } else
- if (cmd == 0x83) // volume
- {
- m->volcmd = VOLCMD_VOLUME;
- m->vol = arg;
- } else
- // effect
- {
- UINT command = cmd & 0x7F;
- UINT param = arg;
- switch(command)
+ uint8 data[4];
+ file.ReadArray(data);
+
+ p->note = NOTE_NONE;
+ if(data[0])
{
- // 0x01: Set Speed
- case 0x01: command = CMD_SPEED; break;
- // 0x02: Volume Slide
- // 0x0A: Tone Porta + Vol Slide
- // 0x0B: Vibrato + Vol Slide
- case 0x02: command = CMD_VOLUMESLIDE;
- case 0x0A: if (command == 0x0A) command = CMD_TONEPORTAVOL;
- case 0x0B: if (command == 0x0B) command = CMD_VIBRATOVOL;
- if (param & 0x80) param = (-(signed char)param)&0x0F;
- else param = (param&0x0F)<<4;
- break;
- // 0x04: Porta Up/Down
- case 0x04: if (param & 0x80) { command = CMD_PORTAMENTOUP; param = (-(signed char)param)&0x7F; }
- else { command = CMD_PORTAMENTODOWN; }
- break;
- // 0x06: Tone Portamento
- case 0x06: command = CMD_TONEPORTAMENTO; break;
- // 0x07: Tremor
- case 0x07: command = CMD_TREMOR; break;
- // 0x08: Arpeggio
- case 0x08: command = CMD_ARPEGGIO; break;
- // 0x09: Vibrato
- case 0x09: command = CMD_VIBRATO; break;
- // 0x0C: Pattern Break
- case 0x0C: command = CMD_PATTERNBREAK; break;
- // 0x0D: Position Jump
- case 0x0D: command = CMD_POSITIONJUMP; break;
- // 0x0F: Retrig
- case 0x0F: command = CMD_RETRIG; break;
- // 0x10: Offset
- case 0x10: command = CMD_OFFSET; break;
- // 0x11: Fine Volume Slide
- case 0x11: if (param) { command = CMD_VOLUMESLIDE;
- if (param & 0x80) param = 0xF0|((-(signed char)param)&0x0F);
- else param = 0x0F|((param&0x0F)<<4);
- } else command = 0; break;
- // 0x12: Fine Portamento
- // 0x16: Extra Fine Portamento
- case 0x12:
- case 0x16: if (param) { int mask = (command == 0x16) ? 0xE0 : 0xF0;
- command = (param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN;
- if (param & 0x80) param = mask|((-(signed char)param)&0x0F);
- else param |= mask;
- } else command = 0; break;
- // 0x13: Note Delay
- case 0x13: command = CMD_S3MCMDEX; param = 0xD0|(param & 0x0F); break;
- // 0x14: Note Cut
- case 0x14: command = CMD_S3MCMDEX; param = 0xC0|(param & 0x0F); break;
- // 0x15: Set Tempo
- case 0x15: command = CMD_TEMPO; break;
- // 0x17: Panning
- case 0x17: param = (param + 64) & 0x7F;
- if (m->command) { if (!m->volcmd) { m->volcmd = VOLCMD_PANNING; m->vol = param >> 1; } command = 0; }
- else { command = CMD_PANNING8; }
- break;
- // Unknown effects
- default: command = param = 0;
+ p->note = data[0] + 12 + NOTE_MIN;
}
- if (command)
- {
- m->command = command;
- m->param = param;
- }
+ p->instr = data[1];
+ p->command = data[2];
+ p->param = data[3];
+ ConvertModCommand(*p);
}
- pTrack += 3;
}
+
+ // Read Sample Data
+ const SampleIO sampleIO(
+ SampleIO::_8bit,
+ SampleIO::mono,
+ SampleIO::littleEndian,
+ SampleIO::signedPCM);
+
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
+ {
+ sampleIO.ReadSample(Samples[smp], file);
+ }
+ return true;
+
}
+#pragma pack(push, 1)
-bool CSoundFile::ReadAMF(const LPCBYTE lpStream, const DWORD dwMemLength)
-//-----------------------------------------------------------------------
+// DSMI AMF File Header
+struct AMFFileHeader
{
- const AMFFILEHEADER *pfh = (AMFFILEHEADER *)lpStream;
- DWORD dwMemPos;
+ char amf[3];
+ uint8 version;
+ char title[32];
+ uint8 numSamples;
+ uint8 numOrders;
+ uint16 numTracks;
+ uint8 numChannels;
- if ((!lpStream) || (dwMemLength < 2048)) return false;
- if ((!strncmp((LPCSTR)lpStream, "ASYLUM Music Format V1.0", 25)) && (dwMemLength > 4096))
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
{
- UINT numorders, numpats, numsamples;
+ SwapBytesLE(numTracks);
+ }
+};
- dwMemPos = 32;
- numpats = lpStream[dwMemPos+3];
- numorders = lpStream[dwMemPos+4];
- numsamples = 64;
- dwMemPos += 6;
- if ((!numpats) || (numpats > MAX_PATTERNS) || (!numorders)
- || (numpats*64*32 + 294 + 37*64 >= dwMemLength)) return false;
- m_nType = MOD_TYPE_AMF0;
- m_nChannels = 8;
- m_nInstruments = 0;
- m_nSamples = 31;
- m_nDefaultTempo = 125;
- m_nDefaultSpeed = 6;
- Order.ReadAsByte(lpStream + dwMemPos, numorders, dwMemLength - dwMemPos);
- dwMemPos = 294; // ???
- for (UINT iSmp=0; iSmp<numsamples; iSmp++)
+STATIC_ASSERT(sizeof(AMFFileHeader) == 41);
+
+#pragma pack(pop)
+
+
+// Read a single AMF track (channel) into a pattern.
+void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &fileChunk)
+//-----------------------------------------------------------------------------
+{
+ fileChunk.Rewind();
+ ModCommand::INSTR lastInstr = 0;
+ while(fileChunk.BytesLeft())
+ {
+ const uint8 row = fileChunk.ReadUint8();
+ const uint8 command = fileChunk.ReadUint8();
+ const uint8 value = fileChunk.ReadUint8();
+ if(row >= pattern.GetNumRows())
{
- ModSample *psmp = &Samples[iSmp+1];
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[iSmp + 1], reinterpret_cast<const char *>(lpStream + dwMemPos), 22);
+ break;
+ }
- psmp->nGlobalVol = 64;
- psmp->nFineTune = MOD2XMFineTune(lpStream[dwMemPos+22]);
- psmp->nVolume = lpStream[dwMemPos+23];
- if (psmp->nVolume > 0x40) psmp->nVolume = 0x40;
- psmp->nVolume <<= 2;
- psmp->RelativeTone = (signed char)lpStream[dwMemPos + 24];
- psmp->nLength = LittleEndian(*((LPDWORD)(lpStream+dwMemPos+25)));
- psmp->nLoopStart = LittleEndian(*((LPDWORD)(lpStream+dwMemPos+29)));
- psmp->nLoopEnd = psmp->nLoopStart + LittleEndian(*((LPDWORD)(lpStream+dwMemPos+33)));
- if (psmp->nLoopEnd <= 2) psmp->nLoopEnd = 0;
- if ((psmp->nLoopEnd > psmp->nLoopStart) && (psmp->nLoopEnd <= psmp->nLength))
+ ModCommand &m = *pattern.GetpModCommand(row, chn);
+ if(command < 0x7F)
+ {
+ // Note + Volume
+ if(command == 0 && value == 0)
{
- psmp->uFlags = CHN_LOOP;
+ m.note = NOTE_NOTECUT;
} else
{
- psmp->nLoopStart = psmp->nLoopEnd = 0;
+ m.note = command + NOTE_MIN;
+ if(value != 0xFF)
+ {
+ if(!m.instr) m.instr = lastInstr;
+ m.volcmd = VOLCMD_VOLUME;
+ m.vol = value;
+ }
}
- if ((psmp->nLength) && (iSmp>31)) m_nSamples = iSmp+1;
- dwMemPos += 37;
- }
- for (UINT iPat=0; iPat<numpats; iPat++)
+ } else if(command == 0x7F)
{
- Patterns.Insert(iPat, 64);
- ModCommand* p = Patterns[iPat];
- if (!p) break;
- const UCHAR *pin = lpStream + dwMemPos;
- for (UINT i=0; i<8*64; i++)
+ // Duplicate row
+ int8 rowDelta = static_cast<int8>(value);
+ int16 copyRow = static_cast<int16>(row) + rowDelta;
+ if(copyRow >= 0 && copyRow < static_cast<int16>(pattern.GetNumRows()))
{
- p->note = NOTE_NONE;
- if (pin[0])
+ m = *pattern.GetpModCommand(copyRow, chn);
+ }
+ } else if(command == 0x80)
+ {
+ // Instrument
+ m.instr = value + 1;
+ lastInstr = m.instr;
+ } else
+ {
+ // Effect
+ static const ModCommand::COMMAND effTrans[] =
+ {
+ CMD_NONE, CMD_SPEED, CMD_VOLUMESLIDE, CMD_VOLUME,
+ CMD_PORTAMENTOUP, CMD_NONE, CMD_TONEPORTAMENTO, CMD_TREMOR,
+ CMD_ARPEGGIO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL,
+ CMD_PATTERNBREAK, CMD_POSITIONJUMP, CMD_NONE, CMD_RETRIG,
+ CMD_OFFSET, CMD_VOLUMESLIDE, CMD_PORTAMENTOUP, CMD_S3MCMDEX,
+ CMD_S3MCMDEX, CMD_TEMPO, CMD_PORTAMENTOUP, CMD_PANNING8,
+ };
+
+ uint8 cmd = (command & 0x7F);
+ uint8 param = value;
+
+ if(cmd < CountOf(effTrans))
+ {
+ cmd = effTrans[cmd];
+ } else
+ {
+ cmd = CMD_NONE;
+ }
+
+ // Fix some commands...
+ switch(command & 0x7F)
+ {
+ // 02: Volume Slide
+ // 0A: Tone Porta + Vol Slide
+ // 0B: Vibrato + Vol Slide
+ case 0x02:
+ case 0x0A:
+ case 0x0B:
+ if(param & 0x80)
+ param = (-static_cast<int8>(param)) & 0x0F;
+ else
+ param = (param & 0x0F) << 4;
+ break;
+
+ // 03: Volume
+ case 0x03:
+ param = Util::Min(param, uint8(64));
+ if(m.volcmd == VOLCMD_NONE || m.volcmd == VOLCMD_VOLUME)
{
- p->note = pin[0] + 13;
+ m.volcmd = VOLCMD_VOLUME;
+ m.vol = param;
+ cmd = CMD_NONE;
}
- p->instr = pin[1];
- p->command = pin[2];
- p->param = pin[3];
- if (p->command > 0x0F)
+ break;
+
+ // 04: Porta Up/Down
+ case 0x04:
+ if(param & 0x80)
+ param = (-static_cast<int8>(param)) & 0x7F;
+ else
+ cmd = CMD_PORTAMENTODOWN;
+ break;
+
+ // 11: Fine Volume Slide
+ case 0x11:
+ if(param)
{
- #ifdef AMFLOG
- Log("0x%02X.0x%02X ?", p->command, p->param);
- #endif
- p->command = 0;
+ if(param & 0x80)
+ param = 0xF0 | ((-static_cast<int8>(param)) & 0x0F);
+ else
+ param = 0x0F | ((param & 0x0F) << 4);
+ } else
+ {
+ cmd = CMD_NONE;
}
- ConvertModCommand(*p);
- pin += 4;
- p++;
+ break;
+
+ // 12: Fine Portamento
+ // 16: Extra Fine Portamento
+ case 0x12:
+ case 0x16:
+ if(param)
+ {
+ cmd = static_cast<uint8>((param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN);
+ if(param & 0x80)
+ {
+ param = ((-static_cast<int8>(param)) & 0x0F);
+ }
+ param |= (command == 0x16) ? 0xE0 : 0xF0;
+ } else
+ {
+ cmd = CMD_NONE;
+ }
+ break;
+
+ // 13: Note Delay
+ case 0x13:
+ param = 0xD0 | (param & 0x0F);
+ break;
+
+ // 14: Note Cut
+ case 0x14:
+ param = 0xC0 | (param & 0x0F);
+ break;
+
+ // 17: Panning
+ case 0x17:
+ param = (param + 64) & 0x7F;
+ if(m.command != CMD_NONE)
+ {
+ if(m.volcmd == VOLCMD_NONE || m.volcmd == VOLCMD_PANNING)
+ {
+ m.volcmd = VOLCMD_PANNING;
+ m.vol = param / 2;
+ }
+ cmd = CMD_NONE;
+ }
+ break;
}
- dwMemPos += 64*32;
- }
- // Read samples
- const SampleIO sampleIO(
- SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::signedPCM);
-
- for (UINT iData=0; iData<m_nSamples; iData++)
- {
- ModSample &sample = Samples[iData + 1];
- if(sample.nLength)
+ if(cmd != CMD_NONE)
{
- if(dwMemPos > dwMemLength) return false;
- dwMemPos += sampleIO.ReadSample(sample, (LPCSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos);
+ m.command = cmd;
+ m.param = param;
}
}
- return true;
}
- ////////////////////////////
- // DSM/AMF
- USHORT *ptracks[MAX_PATTERNS];
- DWORD sampleseekpos[MAX_SAMPLES];
+}
- if ((pfh->szAMF[0] != 'A') || (pfh->szAMF[1] != 'M') || (pfh->szAMF[2] != 'F')
- || (pfh->version < 10) || (pfh->version > 14) || (!LittleEndianW(pfh->numtracks))
- || (!pfh->numorders) || (pfh->numorders > MAX_PATTERNS)
- || (!pfh->numsamples) || (pfh->numsamples > MAX_SAMPLES)
- || (pfh->numchannels < 1) || (pfh->numchannels > 32))
+
+bool CSoundFile::ReadAMF_DSMI(FileReader &file)
+//---------------------------------------------
+{
+ file.Rewind();
+
+ AMFFileHeader fileHeader;
+ if(!file.ReadConvertEndianness(fileHeader)
+ || memcmp(fileHeader.amf, "AMF", 3)
+ || fileHeader.version < 8 || fileHeader.version > 14
+ || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10))
+ {
return false;
+ }
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], pfh->title);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.title);
- dwMemPos = sizeof(AMFFILEHEADER);
+ if(fileHeader.version < 10)
+ {
+ // Old format revisions are fixed to 4 channels
+ fileHeader.numChannels = 4;
+ file.SkipBack(1);
+ SetupMODPanning(true);
+ }
+
m_nType = MOD_TYPE_AMF;
- m_nChannels = pfh->numchannels;
- m_nSamples = pfh->numsamples;
+ m_nChannels = fileHeader.numChannels;
+ m_nSamples = fileHeader.numSamples;
m_nInstruments = 0;
+
// Setup Channel Pan Positions
- if (pfh->version >= 11)
+ if(fileHeader.version >= 11)
{
- signed char *panpos = (signed char *)(lpStream + dwMemPos);
- UINT nchannels = (pfh->version >= 13) ? 32 : 16;
- for (UINT i=0; i<nchannels; i++)
+ const CHANNELINDEX readChannels = fileHeader.version >= 12 ? 32 : 16;
+ for(CHANNELINDEX chn = 0; chn < readChannels; chn++)
{
- int pan = (panpos[i] + 64) * 2;
- if (pan < 0) pan = 0;
- if (pan > 256) { pan = 128; ChnSettings[i].dwFlags = CHN_SURROUND; }
- ChnSettings[i].nPan = pan;
+ int16 pan = (file.ReadInt8() + 64) * 2;
+ if(pan < 0) pan = 0;
+ if(pan > 256)
+ {
+ pan = 128;
+ ChnSettings[chn].dwFlags = CHN_SURROUND;
+ }
+ ChnSettings[chn].nPan = static_cast<uint16>(pan);
}
- dwMemPos += nchannels;
- } else
+ } else if(fileHeader.version == 10)
{
- for (UINT i=0; i<16; i++)
+ uint8 panPos[16];
+ file.ReadArray(panPos);
+ for(CHANNELINDEX chn = 0; chn < 16; chn++)
{
- ChnSettings[i].nPan = (lpStream[dwMemPos + i] & 1) ? 0x40 : 0xC0;
+ ChnSettings[chn].nPan = (panPos[chn] & 1) ? 0x40 : 0xC0;
}
- dwMemPos += 16;
}
+ // To check: Was the channel table introduced in revision 1.0 or 0.9? I only have 0.8 files, in which it is missing...
+ ASSERT(fileHeader.version != 9);
+
// Get Tempo/Speed
- m_nDefaultTempo = 125;
- m_nDefaultSpeed = 6;
- if (pfh->version >= 13)
+ if(fileHeader.version >= 13)
{
- if (lpStream[dwMemPos] >= 32) m_nDefaultTempo = lpStream[dwMemPos];
- if (lpStream[dwMemPos+1] <= 32) m_nDefaultSpeed = lpStream[dwMemPos+1];
- dwMemPos += 2;
+ m_nDefaultTempo = file.ReadUint8();
+ m_nDefaultSpeed = file.ReadUint8();
+ if(m_nDefaultTempo < 32)
+ {
+ m_nDefaultTempo = 125;
+ }
+ } else
+ {
+ m_nDefaultTempo = 125;
+ m_nDefaultSpeed = 6;
}
- // Setup sequence list
- Order.resize(pfh->numorders, Order.GetInvalidPatIndex());
- vector<ROWINDEX> patternLength(pfh->numorders, 64);
- for (UINT iOrd=0; iOrd < pfh->numorders; iOrd++)
+
+ // Setup Order List
+ Order.resize(fileHeader.numOrders);
+ vector<ROWINDEX> patternLength(fileHeader.numOrders, 64);
+ const size_t trackStartPos = file.GetPosition() + (fileHeader.version >= 14 ? 2 : 0);
+
+ for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++)
{
- Order[iOrd] = iOrd;
- if (pfh->version >= 14)
+ Order[ord] = ord;
+ if(fileHeader.version >= 14)
{
- patternLength[iOrd] = LittleEndianW(*(uint16 *)(lpStream+dwMemPos));
- dwMemPos += 2;
+ patternLength[ord] = file.ReadUint16LE();
}
- ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos);
- dwMemPos += m_nChannels * sizeof(USHORT);
+ // Track positions will be read as needed.
+ file.Skip(m_nChannels * 2);
}
- if (dwMemPos + m_nSamples * (sizeof(AMFSAMPLE)+8) > dwMemLength) return true;
- // Read Samples
- UINT maxsampleseekpos = 0;
- for (UINT iIns=0; iIns<m_nSamples; iIns++)
+
+ // Read Sample Headers
+ vector<uint32> samplePos(GetNumSamples(), 0);
+ uint32 maxSamplePos = 0;
+
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
- ModSample *pSmp = &Samples[iIns+1];
- AMFSAMPLE *psh = (AMFSAMPLE *)(lpStream + dwMemPos);
+ ModSample &sample = Samples[smp];
+ sample.Initialize();
- dwMemPos += sizeof(AMFSAMPLE);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[iIns + 1], psh->samplename);
- StringFixer::ReadString<StringFixer::nullTerminated>(pSmp->filename, psh->filename);
+ uint8 type = file.ReadUint8();
+ file.ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], 32);
+ file.ReadString<StringFixer::nullTerminated>(sample.filename, 13);
+ samplePos[smp - 1] = file.ReadUint32LE();
+ if(fileHeader.version < 10)
+ {
+ sample.nLength = file.ReadUint16LE();
+ } else
+ {
+ sample.nLength = file.ReadUint32LE();
+ }
- pSmp->nLength = LittleEndian(psh->length);
- pSmp->nC5Speed = LittleEndianW(psh->c2spd);
- pSmp->nGlobalVol = 64;
- pSmp->nVolume = psh->volume * 4;
- if (pfh->version >= 11)
+ sample.nC5Speed = file.ReadUint16LE();
+ sample.nVolume = Util::Min(file.ReadUint8(), uint8(64)) * 4u;
+
+ if(fileHeader.version < 10)
{
- pSmp->nLoopStart = LittleEndian(*(DWORD *)(lpStream+dwMemPos));
- pSmp->nLoopEnd = LittleEndian(*(DWORD *)(lpStream+dwMemPos+4));
- dwMemPos += 8;
+ // Various sources (Miodrag Vallat's amf.txt, old ModPlug code) suggest that the loop information
+ // format revision 1.0 should only consist of a 16-bit value for the loop start (loop end would
+ // automatically equal sample length), but the only v1.0 files I have ("the tribal zone" and
+ // "the way its gonna b" by Maelcum) do not confirm this - the sample headers are laid out exactly
+ // as in the newer revisions in these two files. Even in format revision 0.8 (output by MOD2AMF v1.02)
+ // There are loop start and loop end values (although they are 16-Bit). Maybe this only applies to
+ // even older revision of the format?
+ sample.nLoopStart = file.ReadUint16LE();
+ sample.nLoopEnd = file.ReadUint16LE();
} else
{
- pSmp->nLoopStart = LittleEndianW(*(WORD *)(lpStream+dwMemPos));
- pSmp->nLoopEnd = pSmp->nLength;
- dwMemPos += 2;
+ sample.nLoopStart = file.ReadUint32LE();
+ sample.nLoopEnd = file.ReadUint32LE();
}
- sampleseekpos[iIns] = 0;
- if ((psh->type) && (LittleEndian(psh->offset) < dwMemLength-1))
+
+ // Length of v1.0+ sample header: 65 bytes
+ // Length of old sample header: 59 bytes
+
+ if(type != 0)
{
- sampleseekpos[iIns] = LittleEndian(psh->offset);
- if (LittleEndian(psh->offset) > maxsampleseekpos)
- maxsampleseekpos = LittleEndian(psh->offset);
- if ((pSmp->nLoopEnd > pSmp->nLoopStart + 2)
- && (pSmp->nLoopEnd <= pSmp->nLength)) pSmp->uFlags |= CHN_LOOP;
+ if(sample.nLoopEnd > sample.nLoopStart + 2 && sample.nLoopEnd <= sample.nLength)
+ {
+ sample.uFlags.set(CHN_LOOP);
+ } else
+ {
+ sample.nLoopStart = sample.nLoopEnd = 0;
+ }
+
+ maxSamplePos = Util::Max(maxSamplePos, samplePos[smp - 1]);
}
}
+
// Read Track Mapping Table
- USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos);
- UINT realtrackcnt = 0;
- dwMemPos += pfh->numtracks * sizeof(USHORT);
- for (UINT iTrkMap=0; iTrkMap<pfh->numtracks; iTrkMap++)
+ vector<uint16> trackMap;
+ file.ReadVector(trackMap, fileHeader.numTracks);
+ uint16 trackCount = 0;
+ for(vector<uint16>::const_iterator i = trackMap.begin(); i != trackMap.end(); i++)
{
- if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap];
+ trackCount = Util::Max(trackCount, *i);
}
- // Store tracks positions
- vector<BYTE *>pTrackData(realtrackcnt, 0);
- for (UINT iTrack=0; iTrack<realtrackcnt; iTrack++) if (dwMemPos <= dwMemLength-3)
+
+ // Store Tracks Positions
+ vector<FileReader> trackData(trackCount);
+ for(uint16 i = 0; i < trackCount; i++)
{
- UINT nTrkSize = LittleEndianW(*(USHORT *)(lpStream+dwMemPos));
- nTrkSize += (UINT)lpStream[dwMemPos+2] << 16;
- if (dwMemPos + nTrkSize * 3 + 3 <= dwMemLength)
+ // Track size is a 24-Bit value describing the number of byte triplets in this track.
+ uint32 trackSize = file.ReadUint16LE() | (file.ReadUint8() << 16);
+ trackData[i] = file.GetChunk(trackSize * 3);
+ }
+
+ // Read Sample Data
+ const SampleIO sampleIO(
+ SampleIO::_8bit,
+ SampleIO::mono,
+ SampleIO::littleEndian,
+ SampleIO::unsignedPCM);
+
+ // Why is all of this sample loading business so dumb in AMF?
+ // Surely there must be some great idea behind it which isn't handled here (re-using the same sample data for different sample slots maybe?)
+ for(uint32 seekPos = 1; seekPos <= maxSamplePos; seekPos++)
+ {
+ for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
{
- pTrackData[iTrack] = (BYTE *)(lpStream + dwMemPos);
+ if(seekPos == samplePos[smp])
+ {
+ sampleIO.ReadSample(Samples[smp + 1], file);
+ break;
+ }
}
- dwMemPos += nTrkSize * 3 + 3;
+ if(!file.BytesLeft())
+ {
+ break;
+ }
}
+
// Create the patterns from the list of tracks
- for (UINT iPat=0; iPat<pfh->numorders; iPat++)
+ for(PATTERNINDEX pat = 0; pat < fileHeader.numOrders; pat++)
{
- if(Patterns.Insert(iPat, patternLength[iPat]))
+ if(Patterns.Insert(pat, patternLength[pat]))
{
- break;
+ continue;
}
- for (UINT iChn=0; iChn<m_nChannels; iChn++)
+
+ // Get table with per-channel track assignments
+ file.Seek(trackStartPos + pat * (GetNumChannels() * 2 + (fileHeader.version >= 14 ? 2 : 0)));
+ vector<uint16> tracks;
+ file.ReadVector(tracks, GetNumChannels());
+
+ for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
{
- UINT nTrack = LittleEndianW(ptracks[iPat][iChn]);
- if ((nTrack) && (nTrack <= pfh->numtracks))
+ if(tracks[chn] > 0 && tracks[chn] <= fileHeader.numTracks)
{
- UINT realtrk = LittleEndianW(pTrackMap[nTrack-1]);
- if (realtrk)
+ uint16 realTrack = trackMap[tracks[chn] - 1];
+ if(realTrack > 0 && realTrack <= trackCount)
{
- realtrk--;
- if ((realtrk < realtrackcnt) && (pTrackData[realtrk]))
- {
- AMF_Unpack(Patterns[iPat].GetpModCommand(0, iChn), pTrackData[realtrk], Patterns[iPat].GetNumRows(), m_nChannels);
- }
+ realTrack--;
+ AMFReadPattern(Patterns[pat], chn, trackData[realTrack]);
}
}
}
}
- // Read Sample Data
- const SampleIO sampleIO(
- SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::unsignedPCM);
-
- for (UINT iSeek=1; iSeek<=maxsampleseekpos; iSeek++)
- {
- if (dwMemPos >= dwMemLength) break;
- for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (iSeek == sampleseekpos[iSmp])
- {
- dwMemPos += sampleIO.ReadSample(Samples[iSmp + 1], (LPCSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
- break;
- }
- }
return true;
}
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-08-05 18:38:07 UTC (rev 1341)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-08-05 18:41:19 UTC (rev 1342)
@@ -1001,7 +1001,7 @@
}
}
- if (GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED))
+ if(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2|MOD_TYPE_MED|MOD_TYPE_MOD))
{
note += pChn->nTranspose;
// RealNote = PatternNote + RelativeTone; (0..118, 0 = C-0, 118 = A#9)
@@ -1290,8 +1290,8 @@
// Copy Channel
*p = *pChn;
p->dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO | CHN_PANBRELLO | CHN_MUTE | CHN_PORTAMENTO);
- p->nMasterChn = nChn+1;
- p->nCommand = 0;
+ p->nMasterChn = nChn + 1;
+ p->nCommand = CMD_NONE;
// Cut the note
p->nFadeOutVol = 0;
p->dwFlags.set(CHN_NOTEFADE | CHN_FASTVOLRAMP);
@@ -1449,7 +1449,7 @@
p->dwFlags.set(pChn->dwFlags & (CHN_MUTE | CHN_NOFX));
p->nMasterChn = nChn + 1;
- p->nCommand = 0;
+ p->nCommand = CMD_NONE;
//rewbs.VSTiNNA
if(applyNNAtoPlug && pPlugin)
{
@@ -1462,8 +1462,8 @@
case NNA_NOTECUT:
case NNA_NOTEFADE:
//switch off note played on this plugin, on this tracker channel and midi channel
- //pPlugin->MidiCommand(pChn->pModInstrument->nMidiChannel, pChn->pModInstrument->nMidiProgram, pChn->nNote+0xFF, 0, n);
- pPlugin->MidiCommand(GetBestMidiChannel(nChn), pChn->pModInstrument->nMidiProgram, pChn->pModInstrument->wMidiBank, /*pChn->nNote+*/NOTE_KEYOFF, 0, nChn);
+ //pPlugin->MidiCommand(pChn->pModInstrument->nMidiChannel, pChn->pModInstrument->nMidiProgram, pChn->nNote + NOTE_MAX_SPECIAL, 0, n);
+ pPlugin->MidiCommand(GetBestMidiChannel(nChn), pChn->pModInstrument->nMidiProgram, pChn->pModInstrument->wMidiBank, /*pChn->nNote+*/NOTE_MAX_SPECIAL, 0, nChn);
break;
}
}
@@ -1568,7 +1568,7 @@
{
pChn->ClearRowCmd();
instr = 0;
- volcmd = 0;
+ volcmd = VOLCMD_NONE;
vol = 0;
cmd = 0;
param = 0;
@@ -4149,7 +4149,7 @@
IMixPlugin *pPlug = (IMixPlugin*)m_MixPlugins[nPlug-1].pMixPlugin;
if (pPlug)
{
- pPlug->MidiCommand(GetBestMidiChannel(nChn), pIns->nMidiProgram, pIns->wMidiBank, /*pChn->nNote+*/NOTE_KEYOFF, 0, nChn);
+ pPlug->MidiCommand(GetBestMidiChannel(nChn), pIns->nMidiProgram, pIns->wMidiBank, /*pChn->nNote+*/NOTE_MAX_SPECIAL, 0, nChn);
}
}
}
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-08-05 18:38:07 UTC...
[truncated message content] |
|
From: <sag...@us...> - 2012-09-20 23:43:59
|
Revision: 1354
http://modplug.svn.sourceforge.net/modplug/?rev=1354&view=rev
Author: saga-games
Date: 2012-09-20 23:43:52 +0000 (Thu, 20 Sep 2012)
Log Message:
-----------
[Fix] FT2 Compatibility: All fine slide commands (E1x, E2x, EAx, EBx, X1x, X2x) are supposed to have their own memory. Test cases: Porta-LinkMem.xm, FineVol-LinkMem.xm
Modified Paths:
--------------
trunk/OpenMPT/soundlib/ModChannel.h
trunk/OpenMPT/soundlib/Snd_fx.cpp
Modified: trunk/OpenMPT/soundlib/ModChannel.h
===================================================================
--- trunk/OpenMPT/soundlib/ModChannel.h 2012-09-19 08:44:17 UTC (rev 1353)
+++ trunk/OpenMPT/soundlib/ModChannel.h 2012-09-20 23:43:52 UTC (rev 1354)
@@ -86,7 +86,7 @@
uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros
uint8 nNewNote, nNewIns, nCommand, nArpeggio;
uint8 nOldVolumeSlide, nOldFineVolUpDown;
- uint8 nOldPortaUpDown, nOldFinePortaUpDown;
+ uint8 nOldPortaUpDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown;
uint8 nOldPanSlide, nOldChnVolSlide;
uint8 nVibratoType, nVibratoSpeed, nVibratoDepth;
uint8 nTremoloType, nTremoloSpeed, nTremoloDepth;
Modified: trunk/OpenMPT/soundlib/Snd_fx.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-09-19 08:44:17 UTC (rev 1353)
+++ trunk/OpenMPT/soundlib/Snd_fx.cpp 2012-09-20 23:43:52 UTC (rev 1354)
@@ -2710,10 +2710,16 @@
void CSoundFile::FinePortamentoUp(ModChannel *pChn, UINT param)
//-------------------------------------------------------------
{
- if(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))
+ if(GetType() == MOD_TYPE_XM)
{
- if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
+ // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
+ // Test case: Porta-LinkMem.xm
+ if(param) pChn->nOldFinePortaUpDown = (pChn->nOldFinePortaUpDown & 0x0F) | (param << 4); else param = (pChn->nOldFinePortaUpDown >> 4);
+ } else if(GetType() == MOD_TYPE_MT2)
+ {
+ if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
}
+
if(m_SongFlags[SONG_FIRSTTICK])
{
if ((pChn->nPeriod) && (param))
@@ -2739,10 +2745,16 @@
void CSoundFile::FinePortamentoDown(ModChannel *pChn, UINT param)
//---------------------------------------------------------------
{
- if(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
+ if(GetType() == MOD_TYPE_XM)
{
- if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
+ // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
+ // Test case: Porta-LinkMem.xm
+ if(param) pChn->nOldFinePortaUpDown = (pChn->nOldFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (pChn->nOldFinePortaUpDown & 0x0F);
+ } else if(GetType() == MOD_TYPE_MT2)
+ {
+ if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
}
+
if(m_SongFlags[SONG_FIRSTTICK])
{
if ((pChn->nPeriod) && (param))
@@ -2768,10 +2780,16 @@
void CSoundFile::ExtraFinePortamentoUp(ModChannel *pChn, UINT param)
//------------------------------------------------------------------
{
- if(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))
+ if(GetType() == MOD_TYPE_XM)
{
- if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
+ // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
+ // Test case: Porta-LinkMem.xm
+ if(param) pChn->nOldExtraFinePortaUpDown = (pChn->nOldExtraFinePortaUpDown & 0x0F) | (param << 4); else param = (pChn->nOldExtraFinePortaUpDown >> 4);
+ } else if(GetType() == MOD_TYPE_MT2)
+ {
+ if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
}
+
if(m_SongFlags[SONG_FIRSTTICK])
{
if ((pChn->nPeriod) && (param))
@@ -2797,10 +2815,16 @@
void CSoundFile::ExtraFinePortamentoDown(ModChannel *pChn, UINT param)
//--------------------------------------------------------------------
{
- if(GetType() & (MOD_TYPE_XM|MOD_TYPE_MT2))
+ if(GetType() == MOD_TYPE_XM)
{
- if (param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
+ // FT2 compatibility: E1x / E2x / X1x / X2x memory is not linked
+ // Test case: Porta-LinkMem.xm
+ if(param) pChn->nOldExtraFinePortaUpDown = (pChn->nOldExtraFinePortaUpDown & 0xF0) | (param & 0x0F); else param = (pChn->nOldExtraFinePortaUpDown & 0x0F);
+ } else if(GetType() == MOD_TYPE_MT2)
+ {
+ if(param) pChn->nOldFinePortaUpDown = param; else param = pChn->nOldFinePortaUpDown;
}
+
if(m_SongFlags[SONG_FIRSTTICK])
{
if ((pChn->nPeriod) && (param))
@@ -3133,7 +3157,16 @@
void CSoundFile::FineVolumeUp(ModChannel *pChn, UINT param)
//---------------------------------------------------------
{
- if(param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown;
+ if(GetType() == MOD_TYPE_XM)
+ {
+ // FT2 compatibility: EAx / EBx memory is not linked
+ // Test case: FineVol-LinkMem.xm
+ if(param) pChn->nOldFineVolUpDown = (param << 4) | (pChn->nOldFineVolUpDown & 0x0F); else param = (pChn->nOldFineVolUpDown >> 4);
+ } else
+ {
+ if(param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown;
+ }
+
if(m_SongFlags[SONG_FIRSTTICK])
{
pChn->nVolume += param * 4;
@@ -3146,7 +3179,16 @@
void CSoundFile::FineVolumeDown(ModChannel *pChn, UINT param)
//-----------------------------------------------------------
{
- if(param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown;
+ if(GetType() == MOD_TYPE_XM)
+ {
+ // FT2 compatibility: EAx / EBx memory is not linked
+ // Test case: FineVol-LinkMem.xm
+ if(param) pChn->nOldFineVolUpDown = param | (pChn->nOldFineVolUpDown & 0xF0); else param = (pChn->nOldFineVolUpDown & 0x0F);
+ } else
+ {
+ if(param) pChn->nOldFineVolUpDown = param; else param = pChn->nOldFineVolUpDown;
+ }
+
if(m_SongFlags[SONG_FIRSTTICK])
{
pChn->nVolume -= param * 4;
@@ -4626,7 +4668,7 @@
PLUGINDEX plug = 0;
if(Chn[nChn].pModInstrument != nullptr)
{
- if (respectMutes == RespectMutes && Chn[nChn].pModSample && Chn[nChn].pModSample->uFlags[CHN_MUTE])
+ if(respectMutes == RespectMutes && Chn[nChn].pModSample && Chn[nChn].pModSample->uFlags[CHN_MUTE])
{
plug = 0;
} else
@@ -4665,8 +4707,9 @@
}
+// Get the MIDI channel currently associated with a given tracker channel
uint8 CSoundFile::GetBestMidiChannel(CHANNELINDEX nChn) const
-//----------------------------------------------------------
+//-----------------------------------------------------------
{
if(nChn == CHANNELINDEX_INVALID)
{
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-10-28 22:57:46
|
Revision: 1410
http://modplug.svn.sourceforge.net/modplug/?rev=1410&view=rev
Author: saga-games
Date: 2012-10-28 22:57:35 +0000 (Sun, 28 Oct 2012)
Log Message:
-----------
[Ref] FileReader::ReadMagic doesn't advance cursor anymore if magic couldn't be found (fits the usual usage better)
Modified Paths:
--------------
trunk/OpenMPT/soundlib/FileReader.h
trunk/OpenMPT/soundlib/Load_xm.cpp
Modified: trunk/OpenMPT/soundlib/FileReader.h
===================================================================
--- trunk/OpenMPT/soundlib/FileReader.h 2012-10-28 18:24:37 UTC (rev 1409)
+++ trunk/OpenMPT/soundlib/FileReader.h 2012-10-28 22:57:35 UTC (rev 1410)
@@ -410,16 +410,15 @@
}
// Compare a magic string with the current stream position.
- // Returns true if they are identical.
- // The file cursor is advanced by the the length of the "magic" string.
- bool ReadMagic(const char *magic)
+ // Returns true if they are identical and advances the file cursor by the the length of the "magic" string.
+ // Returns false if the string could not be found. The file cursor is not advanced in this case.
+ bool ReadMagic(const char *const magic)
{
const size_t magicLength = strlen(magic);
- if(CanRead(magicLength))
+ if(CanRead(magicLength) && !memcmp(streamData + streamPos, magic, magicLength))
{
- bool result = !memcmp(streamData + streamPos, magic, magicLength);
streamPos += magicLength;
- return result;
+ return true;
} else
{
return false;
Modified: trunk/OpenMPT/soundlib/Load_xm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_xm.cpp 2012-10-28 18:24:37 UTC (rev 1409)
+++ trunk/OpenMPT/soundlib/Load_xm.cpp 2012-10-28 22:57:35 UTC (rev 1410)
@@ -443,9 +443,6 @@
{
ReadMessage(file, file.ReadUint32LE(), leCR);
madeWith |= verConfirmed;
- } else
- {
- file.SkipBack(4);
}
// Read midi config: "MIDI"
@@ -455,9 +452,6 @@
m_MidiCfg.Sanitize();
m_SongFlags |= SONG_EMBEDMIDICFG;
madeWith |= verConfirmed;
- } else
- {
- file.SkipBack(4);
}
// Read pattern names: "PNAM"
@@ -472,9 +466,6 @@
Patterns[pat].SetName(patName);
}
madeWith |= verConfirmed;
- } else
- {
- file.SkipBack(4);
}
// Read channel names: "CNAM"
@@ -486,9 +477,6 @@
file.ReadString<StringFixer::maybeNullTerminated>(ChnSettings[chn].szName, MAX_CHANNELNAME);
}
madeWith |= verConfirmed;
- } else
- {
- file.SkipBack(4);
}
// Read mix plugins information
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-11-06 00:17:08
|
Revision: 1421
http://modplug.svn.sourceforge.net/modplug/?rev=1421&view=rev
Author: saga-games
Date: 2012-11-06 00:16:59 +0000 (Tue, 06 Nov 2012)
Log Message:
-----------
[Ref] More FileReader magic in CSoundFile::LoadMixPlugins()
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_it.cpp
trunk/OpenMPT/soundlib/Load_itp.cpp
trunk/OpenMPT/soundlib/Load_xm.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Load_it.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_it.cpp 2012-11-05 23:55:16 UTC (rev 1420)
+++ trunk/OpenMPT/soundlib/Load_it.cpp 2012-11-06 00:16:59 UTC (rev 1421)
@@ -574,7 +574,7 @@
// Read mix plugins information
if(file.BytesLeft() > 8)
{
- file.Skip(LoadMixPlugins(file.GetRawData(), file.BytesLeft()));
+ LoadMixPlugins(file);
}
// Read Song Message
@@ -1869,107 +1869,97 @@
#endif // MODPLUG_NO_FILESAVE
-UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen)
-//-----------------------------------------------------------
+void CSoundFile::LoadMixPlugins(FileReader &file)
+//-----------------------------------------------
{
- const BYTE *p = (const BYTE *)pData;
- UINT nPos = 0;
-
- while (nLen - nPos >= 8) // read 4 magic bytes + size
+ while(file.BytesLeft() > 8)
{
- DWORD nPluginSize;
- UINT nPlugin;
+ char code[4];
+ file.ReadArray(code);
+ const uint32 chunkSize = file.ReadUint32LE();
+ if(!file.CanRead(chunkSize))
+ {
+ file.SkipBack(8);
+ return;
+ }
+ FileReader chunk = file.GetChunk(chunkSize);
- nPluginSize = *(DWORD *)(p + nPos + 4);
- if (nPluginSize > nLen - nPos - 8) break;
-
// Channel FX
- if (!memcmp(p + nPos, "CHFX", 4))
+ if(!memcmp(code, "CHFX", 4))
{
- for (size_t ch = 0; ch < MAX_BASECHANNELS; ch++) if (ch * 4 < nPluginSize)
+ for (size_t ch = 0; ch < MAX_BASECHANNELS; ch++)
{
- ChnSettings[ch].nMixPlugin = *(DWORD *)(p + nPos + 8 + ch * 4);
+ ChnSettings[ch].nMixPlugin = chunk.ReadUint32LE();
}
}
// Plugin Data
- else if (memcmp(p + nPos, "FX00", 4) >= 0 && memcmp(p + nPos, "FX99", 4) <= 0)
+ else if(memcmp(code, "FX00", 4) >= 0 && memcmp(code, "FX99", 4) <= 0)
{
+ PLUGINDEX plug = (code[2] - '0') * 10 + (code[3] - '0'); //calculate plug-in number.
- nPlugin = (p[nPos + 2] - '0') * 10 + (p[nPos + 3] - '0'); //calculate plug-in number.
-
- if ((nPlugin < MAX_MIXPLUGINS) && (nPluginSize >= sizeof(SNDMIXPLUGININFO) + 4))
+ if(plug < MAX_MIXPLUGINS)
{
// MPT's standard plugin data. Size not specified in file.. grrr..
- m_MixPlugins[nPlugin].Info = *(const SNDMIXPLUGININFO *)( p +nPos + 8);
- StringFixer::SetNullTerminator(m_MixPlugins[nPlugin].Info.szName);
- StringFixer::SetNullTerminator(m_MixPlugins[nPlugin].Info.szLibraryName);
+ chunk.Read(m_MixPlugins[plug].Info);
+ StringFixer::SetNullTerminator(m_MixPlugins[plug].Info.szName);
+ StringFixer::SetNullTerminator(m_MixPlugins[plug].Info.szLibraryName);
//data for VST setchunk? size lies just after standard plugin data.
- DWORD dwExtra = *(DWORD *)(p+nPos+8+sizeof(SNDMIXPLUGININFO));
+ FileReader pluginDataChunk = chunk.GetChunk(chunk.ReadUint32LE());
- if ((dwExtra) && (dwExtra <= nPluginSize-sizeof(SNDMIXPLUGININFO)-4))
+ if(pluginDataChunk.IsValid())
{
- m_MixPlugins[nPlugin].nPluginDataSize = 0;
- m_MixPlugins[nPlugin].pPluginData = new char [dwExtra];
- if (m_MixPlugins[nPlugin].pPluginData)
+ m_MixPlugins[plug].nPluginDataSize = 0;
+ m_MixPlugins[plug].pPluginData = new char [pluginDataChunk.BytesLeft()];
+ if(m_MixPlugins[plug].pPluginData)
{
- m_MixPlugins[nPlugin].nPluginDataSize = dwExtra;
- memcpy(m_MixPlugins[nPlugin].pPluginData, p+nPos+8+sizeof(SNDMIXPLUGININFO)+4, dwExtra);
+ m_MixPlugins[plug].nPluginDataSize = pluginDataChunk.BytesLeft();
+ memcpy(m_MixPlugins[plug].pPluginData, pluginDataChunk.GetRawData(), pluginDataChunk.BytesLeft());
}
}
//rewbs.modularPlugData
- DWORD dwXPlugData = *(DWORD *)(p + nPos + 8 + sizeof(SNDMIXPLUGININFO) + dwExtra + 4); //read next DWORD into dwMPTExtra
+ FileReader modularData = chunk.GetChunk(chunk.ReadUint32LE());
//if dwMPTExtra is positive and there are dwMPTExtra bytes left in nPluginSize, we have some more data!
- if ((dwXPlugData) && ((int)dwXPlugData <= (int)nPluginSize-(int)(sizeof(SNDMIXPLUGININFO)+dwExtra+8)))
+ if(modularData.IsValid())
{
- DWORD startPos = nPos+8+sizeof(SNDMIXPLUGININFO)+dwExtra+8; // start of extra data for this plug
- DWORD endPos = startPos + dwXPlugData; // end of extra data for this plug
- DWORD currPos = startPos;
-
- while (endPos - currPos >= 4) //cycle through all the bytes
+ while(modularData.BytesLeft() > 4)
{
// do we recognize this chunk?
+ modularData.ReadArray(code);
//rewbs.dryRatio
//TODO: turn this into a switch statement like for modular instrument data
- if (!memcmp(p + currPos, "DWRT", 4))
+ if(!memcmp(code, "DWRT", 4))
{
- currPos += 4; // move past ID
- if (endPos - currPos >= sizeof(float))
- {
- m_MixPlugins[nPlugin].fDryRatio = *(float*) (p+currPos);
- currPos += sizeof(float); //move past data
- }
+ m_MixPlugins[plug].fDryRatio = modularData.ReadFloatLE();
}
//end rewbs.dryRatio
//rewbs.plugDefaultProgram
- else if (!memcmp(p + currPos, "PROG", 4))
+ else if(!memcmp(code, "PROG", 4))
{
- currPos += 4; // move past ID
- if (endPos - currPos >= sizeof(long))
- {
- m_MixPlugins[nPlugin].defaultProgram = *(long*) (p+currPos);
- currPos += sizeof(long); //move past data
- }
+ m_MixPlugins[plug].defaultProgram = modularData.ReadUint32LE();
}
//end rewbs.plugDefaultProgram
//else if.. (add extra attempts to recognize chunks here)
else // otherwise move forward a byte.
{
- currPos++;
+ // Why on earth would you use a modular chunk structure, but not store the size of chunks in the file?!
+ modularData.Skip(1);
}
}
}
//end rewbs.modularPlugData
-
}
+ } else if(!memcmp(code, "XTPM", 4))
+ {
+ // Read too far, chicken out...
+ file.SkipBack(8);
+ return;
}
- nPos += nPluginSize + 8;
}
- return nPos;
}
Modified: trunk/OpenMPT/soundlib/Load_itp.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_itp.cpp 2012-11-05 23:55:16 UTC (rev 1420)
+++ trunk/OpenMPT/soundlib/Load_itp.cpp 2012-11-06 00:16:59 UTC (rev 1421)
@@ -83,8 +83,8 @@
}
// Song mix plugins
- size = Util::Min(file.ReadUint32LE(), file.BytesLeft());
- file.Skip(LoadMixPlugins(file.GetRawData(), size));
+ size = file.ReadUint32LE();
+ LoadMixPlugins(file.GetChunk(size));
// MIDI Macro config
file.ReadStructPartial(m_MidiCfg, file.ReadUint32LE());
Modified: trunk/OpenMPT/soundlib/Load_xm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_xm.cpp 2012-11-05 23:55:16 UTC (rev 1420)
+++ trunk/OpenMPT/soundlib/Load_xm.cpp 2012-11-06 00:16:59 UTC (rev 1421)
@@ -484,7 +484,7 @@
if(file.BytesLeft() >= 8)
{
FileReader::off_t oldPos = file.GetPosition();
- file.Skip(LoadMixPlugins(file.GetRawData(), file.BytesLeft()));
+ LoadMixPlugins(file);
if(file.GetPosition() != oldPos)
{
madeWith |= verConfirmed;
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-11-05 23:55:16 UTC (rev 1420)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-11-06 00:16:59 UTC (rev 1421)
@@ -660,7 +660,7 @@
UINT MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote);
size_t ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers);
- UINT LoadMixPlugins(const void *pData, UINT nLen);
+ void LoadMixPlugins(FileReader &file);
DWORD CutOffToFrequency(UINT nCutOff, int flt_modifier=256) const; // [0-127] => [1-10KHz]
#ifdef MODPLUG_TRACKER
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <sag...@us...> - 2012-12-12 23:02:50
|
Revision: 1459
http://sourceforge.net/p/modplug/code/1459
Author: saga-games
Date: 2012-12-12 23:02:43 +0000 (Wed, 12 Dec 2012)
Log Message:
-----------
[Ref] Rewrote MTM loader to use FileReader class.
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_mo3.cpp
trunk/OpenMPT/soundlib/Load_mtm.cpp
trunk/OpenMPT/soundlib/Sndfile.cpp
trunk/OpenMPT/soundlib/Sndfile.h
Modified: trunk/OpenMPT/soundlib/Load_mo3.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mo3.cpp 2012-12-12 21:07:34 UTC (rev 1458)
+++ trunk/OpenMPT/soundlib/Load_mo3.cpp 2012-12-12 23:02:43 UTC (rev 1459)
@@ -86,7 +86,7 @@
result = ReadXM(unpackedFile)
|| ReadIT(unpackedFile)
|| ReadS3M(unpackedFile)
- || ReadMTM(static_cast<const LPCBYTE>(stream), length)
+ || ReadMTM(unpackedFile)
|| ReadMod(unpackedFile);
}
Modified: trunk/OpenMPT/soundlib/Load_mtm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_mtm.cpp 2012-12-12 21:07:34 UTC (rev 1458)
+++ trunk/OpenMPT/soundlib/Load_mtm.cpp 2012-12-12 23:02:43 UTC (rev 1459)
@@ -14,147 +14,193 @@
#pragma pack(push, 1)
-typedef struct tagMTMSAMPLE
+// File Header
+struct MTMFileHeader
{
+ char id[3]; // MTM file marker
+ uint8 version; // Tracker version
+ char songName[20]; // ASCIIZ songname
+ uint16 numTracks; // Number of tracks saved
+ uint8 lastPattern; // Last pattern number saved
+ uint8 lastOrder; // Last order number to play (songlength-1)
+ uint16 commentSize; // Length of comment field
+ uint8 numSamples; // Number of samples saved
+ uint8 attribute; // Attribute byte (unused)
+ uint8 beatsPerTrack; // Numbers of rows in every pattern (MultiTracker itself does not seem to support values != 64)
+ uint8 numChannels; // Number of channels used
+ uint8 panPos[32]; // Channel pan positions
+
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(numTracks);
+ SwapBytesLE(commentSize);
+ }
+};
+
+STATIC_ASSERT(sizeof(MTMFileHeader) == 66);
+
+
+// Sample Header
+struct MTMSampleHeader
+{
char samplename[22];
uint32 length;
- uint32 reppos;
- uint32 repend;
+ uint32 loopStart;
+ uint32 loopEnd;
int8 finetune;
uint8 volume;
uint8 attribute;
-} MTMSAMPLE;
+ // Convert all multi-byte numeric values to current platform's endianness or vice versa.
+ void ConvertEndianness()
+ {
+ SwapBytesLE(length);
+ SwapBytesLE(loopStart);
+ SwapBytesLE(loopEnd);
+ }
-typedef struct tagMTMHEADER
-{
- char id[3]; // MTM file marker
- uint8 version; // Tracker version
- char songname[20]; // ASCIIZ songname
- uint16 numtracks; // number of tracks saved
- uint8 lastpattern; // last pattern number saved
- uint8 lastorder; // last order number to play (songlength-1)
- uint16 commentsize; // length of comment field
- uint8 numsamples; // number of samples saved
- uint8 attribute; // attribute byte (unused)
- uint8 beatspertrack; // numbers of rows in every pattern
- uint8 numchannels; // number of channels used
- uint8 panpos[32]; // channel pan positions
-} MTMHEADER;
+ // Convert an MTM sample header to OpenMPT's internal sample header.
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize();
+ mptSmp.nVolume = Util::Min(uint16(volume * 4), uint16(256));
+ if(length > 2)
+ {
+ mptSmp.nLength = length;
+ mptSmp.nLoopStart = loopStart;
+ mptSmp.nLoopEnd = loopEnd;
+ LimitMax(mptSmp.nLoopEnd, mptSmp.nLength);
+ if(mptSmp.nLoopStart + 4 >= mptSmp.nLoopEnd) mptSmp.nLoopStart = mptSmp.nLoopEnd = 0;
+ if(mptSmp.nLoopEnd) mptSmp.uFlags.set(CHN_LOOP);
+ mptSmp.nFineTune = MOD2XMFineTune(finetune);
+ mptSmp.nC5Speed = ModSample::TransposeToFrequency(0, mptSmp.nFineTune);
+ if(attribute & 0x01)
+ {
+ mptSmp.uFlags.set(CHN_16BIT);
+ mptSmp.nLength /= 2;
+ mptSmp.nLoopStart /= 2;
+ mptSmp.nLoopEnd /= 2;
+ }
+ }
+ }
+};
+STATIC_ASSERT(sizeof(MTMSampleHeader) == 37);
+
#pragma pack(pop)
-bool CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength)
-//-----------------------------------------------------------
+bool CSoundFile::ReadMTM(FileReader &file)
+//----------------------------------------
{
- DWORD dwMemPos = 66;
+ file.Rewind();
+ MTMFileHeader fileHeader;
+ if(!file.ReadConvertEndianness(fileHeader)
+ || memcmp(fileHeader.id, "MTM", 3)
+ || fileHeader.lastOrder > 127
+ || fileHeader.numChannels > 32
+ || fileHeader.numChannels == 0
+ || fileHeader.numSamples >= MAX_SAMPLES
+ || fileHeader.lastPattern == 0
+ || fileHeader.lastPattern > MAX_PATTERNS
+ || fileHeader.beatsPerTrack == 0
+ || file.BytesLeft() < sizeof(MTMSampleHeader) * fileHeader.numSamples + 128 + 192 * fileHeader.numTracks + 64 * (fileHeader.lastPattern + 1) + fileHeader.commentSize)
+ {
+ return false;
+ }
- if ((!lpStream) || (dwMemLength < 0x100)) return false;
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], fileHeader.songName);
+ m_nType = MOD_TYPE_MTM;
+ m_nSamples = fileHeader.numSamples;
+ m_nChannels = fileHeader.numChannels;
- MTMHEADER *pmh = (MTMHEADER *)lpStream;
- if ((memcmp(pmh->id, "MTM", 3)) || (pmh->numchannels > 32)
- || (pmh->numsamples >= MAX_SAMPLES) || (!pmh->numsamples)
- || (!pmh->numtracks) || (!pmh->numchannels)
- || (!pmh->lastpattern) || (pmh->lastpattern > MAX_PATTERNS)) return false;
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[0], pmh->songname);
-
- if (dwMemPos + 37 * pmh->numsamples + 128 + 192 * pmh->numtracks
- + 64 * (pmh->lastpattern+1) + pmh->commentsize >= dwMemLength) return false;
- m_nType = MOD_TYPE_MTM;
- m_nSamples = pmh->numsamples;
- m_nChannels = pmh->numchannels;
// Reading instruments
- for (SAMPLEINDEX i = 1; i <= m_nSamples; i++)
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
- MTMSAMPLE *pms = (MTMSAMPLE *)(lpStream + dwMemPos);
- StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[i], pms->samplename);
- Samples[i].nVolume = pms->volume << 2;
- Samples[i].nGlobalVol = 64;
- UINT len = pms->length;
- if(len > 2)
- {
- Samples[i].nLength = len;
- Samples[i].nLoopStart = pms->reppos;
- Samples[i].nLoopEnd = pms->repend;
- if (Samples[i].nLoopEnd > Samples[i].nLength) Samples[i].nLoopEnd = Samples[i].nLength;
- if (Samples[i].nLoopStart + 4 >= Samples[i].nLoopEnd) Samples[i].nLoopStart = Samples[i].nLoopEnd = 0;
- if (Samples[i].nLoopEnd) Samples[i].uFlags |= CHN_LOOP;
- Samples[i].nFineTune = MOD2XMFineTune(pms->finetune);
- if (pms->attribute & 0x01)
- {
- Samples[i].uFlags |= CHN_16BIT;
- Samples[i].nLength >>= 1;
- Samples[i].nLoopStart >>= 1;
- Samples[i].nLoopEnd >>= 1;
- }
- Samples[i].nPan = 128;
- Samples[i].nC5Speed = ModSample::TransposeToFrequency(0, Samples[i].nFineTune);
- }
- dwMemPos += 37;
+ MTMSampleHeader sampleHeader;
+ file.ReadConvertEndianness(sampleHeader);
+ sampleHeader.ConvertToMPT(Samples[smp]);
+ StringFixer::ReadString<StringFixer::maybeNullTerminated>(m_szNames[smp], sampleHeader.samplename);
}
+
// Setting Channel Pan Position
- for (CHANNELINDEX ich = 0; ich < m_nChannels; ich++)
+ for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
{
- ChnSettings[ich].nPan = ((pmh->panpos[ich] & 0x0F) << 4) + 8;
- ChnSettings[ich].nVolume = 64;
+ ChnSettings[chn].nPan = ((fileHeader.panPos[chn] & 0x0F) << 4) + 8;
+ ChnSettings[chn].nVolume = 64;
}
+
// Reading pattern order
- Order.ReadAsByte(lpStream + dwMemPos, pmh->lastorder + 1, dwMemLength - dwMemPos);
- dwMemPos += 128;
+ const ORDERINDEX readOrders = fileHeader.lastOrder + 1;
+ Order.ReadAsByte(file, readOrders);
+ file.Skip(128 - readOrders);
+
// Reading Patterns
- ROWINDEX nPatRows = CLAMP(pmh->beatspertrack, 1, MAX_PATTERN_ROWS);
- LPCBYTE pTracks = lpStream + dwMemPos;
- dwMemPos += 192 * pmh->numtracks;
- LPWORD pSeq = (LPWORD)(lpStream + dwMemPos);
- for (PATTERNINDEX pat = 0; pat <= pmh->lastpattern; pat++)
+ const ROWINDEX rowsPerPat = Util::Min(ROWINDEX(fileHeader.beatsPerTrack), MAX_PATTERN_ROWS);
+ FileReader tracks = file.GetChunk(192 * fileHeader.numTracks);
+
+ for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPattern; pat++)
{
- if(Patterns.Insert(pat, nPatRows)) break;
- for (UINT n=0; n<32; n++) if ((pSeq[n]) && (pSeq[n] <= pmh->numtracks) && (n < m_nChannels))
+ if(Patterns.Insert(pat, rowsPerPat))
{
- LPCBYTE p = pTracks + 192 * (pSeq[n]-1);
- ModCommand *m = Patterns[pat] + n;
- for (UINT i = 0; i < nPatRows; i++, m += m_nChannels, p += 3)
+ break;
+ }
+
+ for(CHANNELINDEX chn = 0; chn < 32; chn++)
+ {
+ uint16 track = file.ReadUint16LE();
+ if(track == 0 || track > fileHeader.numTracks || chn >= GetNumChannels())
{
- if (p[0] & 0xFC) m->note = (p[0] >> 2) + 37;
- m->instr = ((p[0] & 0x03) << 4) | (p[1] >> 4);
- uint8 cmd = p[1] & 0x0F;
- uint8 param = p[2];
- if (cmd == 0x0A)
+ continue;
+ }
+
+ tracks.Seek(192 * (track - 1));
+
+ ModCommand *m = Patterns[pat].GetpModCommand(0, chn);
+ for(ROWINDEX row = 0; row < rowsPerPat; row++, m += GetNumChannels())
+ {
+ uint8 data[3];
+ tracks.ReadArray(data);
+
+ if(data[0] & 0xFC) m->note = (data[0] >> 2) + 36 + NOTE_MIN;
+ m->instr = ((data[0] & 0x03) << 4) | (data[1] >> 4);
+ uint8 cmd = data[1] & 0x0F;
+ uint8 param = data[2];
+ if(cmd == 0x0A)
{
- if (param & 0xF0) param &= 0xF0; else param &= 0x0F;
+ if(param & 0xF0) param &= 0xF0; else param &= 0x0F;
}
m->command = cmd;
m->param = param;
- if ((cmd) || (param))
+ if(cmd != 0 || param != 0)
{
ConvertModCommand(*m);
m->Convert(MOD_TYPE_MOD, MOD_TYPE_S3M);
}
}
}
- pSeq += 32;
}
- dwMemPos += 64 * (pmh->lastpattern + 1);
- if ((pmh->commentsize) && (dwMemPos + pmh->commentsize < dwMemLength))
+
+ if(fileHeader.commentSize != 0)
{
- UINT n = pmh->commentsize;
- ReadFixedLineLengthMessage(lpStream + dwMemPos, n, 39, 1);
+ // Read message with a fixed line length of 40 characters
+ // (actually the last character is always null, so make that 39 + 1 padding byte)
+ ReadFixedLineLengthMessage(file, fileHeader.commentSize, 39, 1);
}
- dwMemPos += pmh->commentsize;
+
// Reading Samples
- for (UINT ismp=1; ismp<=m_nSamples; ismp++)
+ for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
- if (dwMemPos >= dwMemLength) break;
-
- dwMemPos += SampleIO(
- (Samples[ismp].uFlags & CHN_16BIT) ? SampleIO::_16bit : SampleIO::_8bit,
+ SampleIO(
+ Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
SampleIO::mono,
SampleIO::littleEndian,
SampleIO::unsignedPCM)
- .ReadSample(Samples[ismp], (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos);
+ .ReadSample(Samples[smp], file);
}
+
m_nMinPeriod = 64;
m_nMaxPeriod = 32767;
return true;
Modified: trunk/OpenMPT/soundlib/Sndfile.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.cpp 2012-12-12 21:07:34 UTC (rev 1458)
+++ trunk/OpenMPT/soundlib/Sndfile.cpp 2012-12-12 23:02:43 UTC (rev 1459)
@@ -609,7 +609,7 @@
&& !ReadSTM(file)
&& !ReadMed(lpStream, dwMemLength)
#ifndef FASTSOUNDLIB
- && !ReadMTM(lpStream, dwMemLength)
+ && !ReadMTM(file)
&& !ReadMDL(lpStream, dwMemLength)
&& !ReadDBM(lpStream, dwMemLength)
&& !Read669(file)
@@ -1573,7 +1573,7 @@
sample.FreeSample();
sample.nLength = 0;
- sample.uFlags &= ~(CHN_16BIT | CHN_STEREO);
+ sample.uFlags.reset(CHN_16BIT | CHN_STEREO);
return true;
}
@@ -1589,7 +1589,7 @@
Samples[from].pSample = nullptr;
Samples[from].nLength = 0;
- Samples[from].uFlags &= ~(CHN_16BIT);
+ Samples[from].uFlags.reset(CHN_16BIT | CHN_STEREO);
return true;
}
Modified: trunk/OpenMPT/soundlib/Sndfile.h
===================================================================
--- trunk/OpenMPT/soundlib/Sndfile.h 2012-12-12 21:07:34 UTC (rev 1458)
+++ trunk/OpenMPT/soundlib/Sndfile.h 2012-12-12 23:02:43 UTC (rev 1459)
@@ -388,7 +388,7 @@
bool ReadS3M(FileReader &file);
bool ReadMod(FileReader &file);
bool ReadMed(const LPCBYTE lpStream, const DWORD dwMemLength);
- bool ReadMTM(const LPCBYTE lpStream, const DWORD dwMemLength);
+ bool ReadMTM(FileReader &file);
bool ReadSTM(FileReader &file);
bool ReadIT(FileReader &file);
bool ReadITProject(FileReader &file);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|